Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15900 articles
Browse latest View live

GitHub - letiantian/TextRank4ZH: 从中文文本中自动提取关键词和摘要

$
0
0

TextRank4ZH

TextRank算法可以用来从文本中提取关键词和摘要(重要的句子)。TextRank4ZH是针对中文文本的TextRank算法的python算法实现。

安装

方式1:

$ python setup.py install --user

方式2:

$ sudo python setup.py install

方式3:

$ pip install textrank4zh --user

方式4:

$ sudo pip install textrank4zh

Python 3下需要将上面的python改成python3,pip改成pip3。

卸载

$ pip uninstall textrank4zh

依赖

jieba >= 0.35

numpy >= 1.7.1

networkx >= 1.9.1

兼容性

在Python 2.7.9和Python 3.4.3中测试通过。

原理

TextRank的详细原理请参考:

Mihalcea R, Tarau P. TextRank: Bringing order into texts[C]. Association for Computational Linguistics, 2004.

关键词提取

将原文本拆分为句子,在每个句子中过滤掉停用词(可选),并只保留指定词性的单词(可选)。由此可以得到句子的集合和单词的集合。

每个单词作为pagerank中的一个节点。设定窗口大小为k,假设一个句子依次由下面的单词组成:

w1, w2, w3, w4, w5, ..., wn

w1, w2, ..., wkw2, w3, ...,wk+1w3, w4, ...,wk+2等都是一个窗口。在一个窗口中的任两个单词对应的节点之间存在一个无向无权的边。

基于上面构成图,可以计算出每个单词节点的重要性。最重要的若干单词可以作为关键词。

关键短语提取

参照关键词提取提取出若干关键词。若原文本中存在若干个关键词相邻的情况,那么这些关键词可以构成一个关键词组。

例如,在一篇介绍支持向量机的文章中,可以找到关键词支持向量,通过关键词组提取,可以得到支持向量机

摘要生成

将每个句子看成图中的一个节点,若两个句子之间有相似性,认为对应的两个节点之间有一个无向有权边,权值是相似度。

通过pagerank算法计算得到的重要性最高的若干句子可以当作摘要。

示例

exampletest

example/example01.py:

#-*- encoding:utf-8 -*-from__future__importprint_functionimportsystry:reload(sys)
    sys.setdefaultencoding('utf-8')except:passimportcodecsfromtextrank4zhimportTextRank4Keyword, TextRank4Sentence
text=codecs.open('../test/doc/01.txt','r','utf-8').read()
tr4w=TextRank4Keyword()
tr4w.analyze(text=text,lower=True,window=2)#py2中text必须是utf8编码的str或者unicode对象,py3中必须是utf8编码的bytes或者str对象print('关键词:')foritemintr4w.get_keywords(20,word_min_len=1):print(item.word, item.weight)print()print('关键短语:')forphraseintr4w.get_keyphrases(keywords_num=20,min_occur_num=2):print(phrase)
tr4s=TextRank4Sentence()
tr4s.analyze(text=text,lower=True,source='all_filters')print()print('摘要:')foritemintr4s.get_key_sentences(num=3):print(item.index, item.weight, item.sentence)#index是语句在文本中位置,weight是权重

运行结果如下:

关键词:
媒体 0.02155864734852778
高圆圆 0.020220281898126486
微 0.01671909730824073
宾客 0.014328439104001788
赵又廷 0.014035488254875914
答谢 0.013759845912857732
谢娜 0.013361244496632448
现身 0.012724133346018603
记者 0.01227742092899235
新人 0.01183128428494362
北京 0.011686712993089671
博 0.011447168887452668
展示 0.010889176260920504
捧场 0.010507502237123278
礼物 0.010447275379792245
张杰 0.009558332870902892
当晚 0.009137982757893915
戴 0.008915271161035208
酒店 0.00883521621207796
外套 0.008822082954131174
关键短语:
微博
摘要:
摘要:
0 0.0709719557171 中新网北京12月1日电(记者 张曦) 30日晚,高圆圆和赵又廷在京举行答谢宴,诸多明星现身捧场,其中包括张杰(微博)、谢娜(微博)夫妇、何炅(微博)、蔡康永(微博)、徐克、张凯丽、黄轩(微博)等
6 0.0541037236415 高圆圆身穿粉色外套,看到大批记者在场露出娇羞神色,赵又廷则戴着鸭舌帽,十分淡定,两人快步走进电梯,未接受媒体采访
27 0.0490428312984 记者了解到,出席高圆圆、赵又廷答谢宴的宾客近百人,其中不少都是女方的高中同学

##使用说明

类TextRank4Keyword、TextRank4Sentence在处理一段文本时会将文本拆分成4种格式:

  • sentences:由句子组成的列表。
  • words_no_filter:对sentences中每个句子分词而得到的两级列表。
  • words_no_stop_words:去掉words_no_filter中的停止词而得到的二维列表。
  • words_all_filters:保留words_no_stop_words中指定词性的单词而得到的二维列表。

例如,对于:

这间酒店位于北京东三环,里面摆放很多雕塑,文艺气息十足。答谢宴于晚上8点开始。
#-*- encoding:utf-8 -*-from__future__importprint_functionimportcodecsfromtextrank4zhimportTextRank4Keyword, TextRank4Sentenceimportsystry:reload(sys)
    sys.setdefaultencoding('utf-8')except:passtext="这间酒店位于北京东三环,里面摆放很多雕塑,文艺气息十足。答谢宴于晚上8点开始。"tr4w=TextRank4Keyword()
tr4w.analyze(text=text,lower=True,window=2)print()print('sentences:')forsintr4w.sentences:print(s)#py2中是unicode类型。py3中是str类型。print()print('words_no_filter')forwordsintr4w.words_no_filter:print('/'.join(words))#py2中是unicode类型。py3中是str类型。print()print('words_no_stop_words')forwordsintr4w.words_no_stop_words:print('/'.join(words))#py2中是unicode类型。py3中是str类型。print()print('words_all_filters')forwordsintr4w.words_all_filters:print('/'.join(words))#py2中是unicode类型。py3中是str类型。

运行结果如下:

sentences:
这间酒店位于北京东三环,里面摆放很多雕塑,文艺气息十足
答谢宴于晚上8点开始
words_no_filter
这/间/酒店/位于/北京/东三环/里面/摆放/很多/雕塑/文艺/气息/十足
答谢/宴于/晚上/8/点/开始
words_no_stop_words
间/酒店/位于/北京/东三环/里面/摆放/很多/雕塑/文艺/气息/十足
答谢/宴于/晚上/8/点
words_all_filters
酒店/位于/北京/东三环/摆放/雕塑/文艺/气息
答谢/宴于/晚上

API

TODO.

类的实现、函数的参数请参考源码注释。

License

MIT


从零开始掌握Python机器学习:十四步教程 - 知乎专栏

$
0
0

Python 可以说是现在最流行的机器学习语言,而且你也能在网上找到大量的资源。你现在也在考虑从 Python 入门机器学习吗?本教程或许能帮你成功上手,从 0 到 1 掌握 Python 机器学习,至于后面再从 1 到 100 变成机器学习专家,就要看你自己的努力了。本教程原文分为两个部分,机器之心在本文中将其进行了整合,原文可参阅:7 Steps to Mastering Machine Learning With Python 和 7 More Steps to Mastering Machine Learning With Python。本教程的作者为 KDnuggets 副主编兼数据科学家 Matthew Mayo。

「开始」往往是最难的,尤其是当选择太多的时候,一个人往往很难下定决定做出选择。本教程的目的是帮助几乎没有 Python 机器学习背景的新手成长为知识渊博的实践者,而且这个过程中仅需要使用免费的材料和资源即可。这个大纲的主要目标是带你了解那些数量繁多的可用资源。毫无疑问,资源确实有很多,但哪些才是最好的呢?哪些是互补的呢?以怎样的顺序学习这些资源才是最合适的呢?

首先,我假设你并不是以下方面的专家:

  • 机器学习

  • Python

  • 任何 Python 的机器学习、科学计算或数据分析库

当然,如果你对前两个主题有一定程度的基本了解就更好了,但那并不是必要的,在早期阶段多花一点点时间了解一下就行了。

基础篇

第一步:基本 Python 技能

如果我们打算利用 Python 来执行机器学习,那么对 Python 有一些基本的了解就是至关重要的。幸运的是,因为 Python 是一种得到了广泛使用的通用编程语言,加上其在科学计算和机器学习领域的应用,所以找到一个初学者教程并不十分困难。你在 Python 和编程上的经验水平对于起步而言是至关重要的。

首先,你需要安装 Python。因为我们后面会用到科学计算和机器学习软件包,所以我建议你安装 Anaconda。这是一个可用于 Linux、OS X 和 Windows 上的工业级的 Python 实现,完整包含了机器学习所需的软件包,包括 numpy、scikit-learn 和 matplotlib。其也包含了 iPython Notebook,这是一个用在我们许多教程中的交互式环境。我推荐安装 Python 2.7。

如果你不懂编程,我建议你从下面的免费在线书籍开始学习,然后再进入后续的材料:

如果你有编程经验,但不懂 Python 或还很初级,我建议你学习下面两个课程:

  • 谷歌开发者 Python 课程(强烈推荐视觉学习者学习):suo.im/toMzq

  • Python 科学计算入门(来自 UCSB Engineering 的 M. Scott Shell)(一个不错的入门,大约有 60 页):suo.im/2cXycM

如果你要 30 分钟上手 Python 的快速课程,看下面:

当然,如果你已经是一位经验丰富的 Python 程序员了,这一步就可以跳过了。即便如此,我也建议你常使用 Python 文档:Welcome to Python.org

第二步:机器学习基础技巧

KDnuggets 的 Zachary Lipton 已经指出:现在,人们评价一个「数据科学家」已经有很多不同标准了。这实际上是机器学习领域领域的一个写照,因为数据科学家大部分时间干的事情都牵涉到不同程度地使用机器学习算法。为了有效地创造和获得来自支持向量机的洞见,非常熟悉核方法(kernel methods)是否必要呢?当然不是。就像几乎生活中的所有事情一样,掌握理论的深度是与实践应用相关的。对机器学习算法的深度了解超过了本文探讨的范围,它通常需要你将非常大量的时间投入到更加学术的课程中去,或者至少是你自己要进行高强度的自学训练。

好消息是,对实践来说,你并不需要获得机器学习博士般的理论理解——就想要成为一个高效的程序员并不必要进行计算机科学理论的学习。

人们对吴恩达在 Coursera 上的机器学习课程内容往往好评如潮;然而,我的建议是浏览前一个学生在线记录的课堂笔记。跳过特定于 Octave(一个类似于 Matlab 的与你 Python 学习无关的语言)的笔记。一定要明白这些都不是官方笔记,但是可以从它们中把握到吴恩达课程材料中相关的内容。当然如果你有时间和兴趣,你现在就可以去 Coursera 上学习吴恩达的机器学习课程:Machine Learning - Stanford University | Coursera

除了上面提到的吴恩达课程,如果你还需要需要其它的,网上还有很多各类课程供你选择。比如我就很喜欢 Tom Mitchell,这里是他最近演讲的视频(一起的还有 Maria-Florina Balcan),非常平易近人。

目前你不需要所有的笔记和视频。一个有效地方法是当你觉得合适时,直接去看下面特定的练习题,参考上述备注和视频恰当的部分,

第三步:科学计算 Python 软件包概述

好了,我们已经掌握了 Python 编程并对机器学习有了一定的了解。而在 Python 之外,还有一些常用于执行实际机器学习的开源软件库。广义上讲,有很多所谓的科学 Python 库(scientific Python libraries)可用于执行基本的机器学习任务(这方面的判断肯定有些主观性):

学习这些库的一个好方法是学习下面的材料:

在本教程的后面你还会看到一些其它的软件包,比如基于 matplotlib 的数据可视化库 Seaborn。前面提到的软件包只是 Python 机器学习中常用的一些核心库的一部分,但是理解它们应该能让你在后面遇到其它软件包时不至于感到困惑。

下面就开始动手吧!

第四步:使用 Python 学习机器学习

首先检查一下准备情况

  • Python:就绪

  • 机器学习基本材料:就绪

  • Numpy:就绪

  • Pandas:就绪

  • Matplotlib:就绪

现在是时候使用 Python 机器学习标准库 scikit-learn 来实现机器学习算法了。

scikit-learn 流程图

下面许多的教程和训练都是使用 iPython (Jupyter) Notebook 完成的,iPython Notebook 是执行 Python 语句的交互式环境。iPython Notebook 可以很方便地在网上找到或下载到你的本地计算机。

同样也请注意,以下的教程是由一系列在线资源所组成。如果你感觉课程有什么不合适的,可以和作者交流。我们第一个教程就是从 scikit-learn 开始的,我建议你们在继续完成教程前可以按顺序看一看以下的文章。

下面是一篇是对 scikit-learn 简介的文章,scikit-learn 是 Python 最常用的通用机器学习库,其覆盖了 K 近邻算法:

下面的会更加深入、扩展的一篇简介,包括了从著名的数据库开始完成一个项目:

下一篇关注于在 scikit-learn 上评估不同模型的策略,包括训练集/测试集的分割方法:

第五步:Python 上实现机器学习的基本算法

在有了 scikit-learn 的基本知识后,我们可以进一步探索那些更加通用和实用的算法。我们从非常出名的 K 均值聚类(k-means clustering)算法开始,它是一种非常简单和高效的方法,能很好地解决非监督学习问题:

接下来我们可以回到分类问题,并学习曾经最流行的分类算法:

在了解分类问题后,我们可以继续看看连续型数值预测:

我们也可以利用回归的思想应用到分类问题中,即 logistic 回归:

第六步:Python 上实现进阶机器学习算法

我们已经熟悉了 scikit-learn,现在我们可以了解一下更高级的算法了。首先就是支持向量机,它是一种依赖于将数据转换映射到高维空间的非线性分类器。

随后,我们可以通过 Kaggle Titanic 竞赛检查学习作为集成分类器的随机森林:

降维算法经常用于减少在问题中所使用的变量。主成份分析法就是非监督降维算法的一个特殊形式:

在进入第七步之前,我们可以花一点时间考虑在相对较短的时间内取得的一些进展。

首先使用 Python 及其机器学习库,我们不仅已经了解了一些最常见和知名的机器学习算法(k 近邻、k 均值聚类、支持向量机等),还研究了强大的集成技术(随机森林)和一些额外的机器学习任务(降维算法和模型验证技术)。除了一些基本的机器学习技巧,我们已经开始寻找一些有用的工具包。

我们会进一步学习新的必要工具。

第七步:Python 深度学习

神经网络包含很多层

深度学习无处不在。深度学习建立在几十年前的神经网络的基础上,但是最近的进步始于几年前,并极大地提高了深度神经网络的认知能力,引起了人们的广泛兴趣。如果你对神经网络还不熟悉,KDnuggets 有很多文章详细介绍了最近深度学习大量的创新、成就和赞许。

最后一步并不打算把所有类型的深度学习评论一遍,而是在 2 个先进的当代 Python 深度学习库中探究几个简单的网络实现。对于有兴趣深挖深度学习的读者,我建议从下面这些免费的在线书籍开始:

1.Theano

链接:Welcome - Theano 0.8.2 documentation

Theano 是我们讲到的第一个 Python 深度学习库。看看 Theano 作者怎么说:

Theano 是一个 Python 库,它可以使你有效地定义、优化和评估包含多维数组的数学表达式。

下面关于运用 Theano 学习深度学习的入门教程有点长,但是足够好,描述生动,评价很高:

2.Caffe 

链接:Caffe | Deep Learning Framework

另一个我们将测试驱动的库是 Caffe。再一次,让我们从作者开始:

Caffe 是一个深度学习框架,由表达、速度和模块性建构,Bwekeley 视觉与学习中心和社区工作者共同开发了 Caf fe。

这个教程是本篇文章中最好的一个。我们已经学习了上面几个有趣的样例,但没有一个可与下面这个样例相竞争,其可通过 Caffe 实现谷歌的 DeepDream。这个相当精彩!掌握教程之后,可以尝试使你的处理器自如运行,就当作是娱乐。

我并没有保证说这会很快或容易,但是如果你投入了时间并完成了上面的 7 个步骤,你将在理解大量机器学习算法以及通过流行的库(包括一些在目前深度学习研究领域最前沿的库)在 Python 中实现算法方面变得很擅长。

进阶篇

机器学习算法

本篇是使用 Python 掌握机器学习的 7 个步骤系列文章的下篇,如果你已经学习了该系列的上篇,那么应该达到了令人满意的学习速度和熟练技能;如果没有的话,你也许应该回顾一下上篇,具体花费多少时间,取决于你当前的理解水平。我保证这样做是值得的。快速回顾之后,本篇文章会更明确地集中于几个机器学习相关的任务集上。由于安全地跳过了一些基础模块——Python 基础、机器学习基础等等——我们可以直接进入到不同的机器学习算法之中。这次我们可以根据功能更好地分类教程。

第1步:机器学习基础回顾&一个新视角

上篇中包括以下几步:

1. Python 基础技能

2. 机器学习基础技能

3. Python 包概述

4. 运用 Python 开始机器学习:介绍&模型评估

5. 关于 Python 的机器学习主题:k-均值聚类、决策树、线性回归&逻辑回归

6. 关于 Python 的高阶机器学习主题:支持向量机、随机森林、PCA 降维

7. Python 中的深度学习

如上所述,如果你正准备从头开始,我建议你按顺序读完上篇。我也会列出所有适合新手的入门材料,安装说明包含在上篇文章中。

然而,如果你已经读过,我会从下面最基础的开始:

如果你正在寻找学习机器学习基础的替代或补充性方法,恰好我可以把正在看的 Shai Ben-David 的视频讲座和 Shai Shalev-Shwartz 的教科书推荐给你:

  • Shai Ben-David 的机器学习介绍视频讲座,滑铁卢大学。地址:suo.im/1TFlK6

  • 理解机器学习:从理论到算法,作者 Shai Ben-David & Shai Shalev-Shwartz。地址:suo.im/1NL0ix

记住,这些介绍性资料并不需要全部看完才能开始我写的系列文章。视频讲座、教科书及其他资源可在以下情况查阅:当使用机器学习算法实现模型时或者当合适的概念被实际应用在后续步骤之中时。具体情况自己判断。

第2步:更多的分类

我们从新材料开始,首先巩固一下我们的分类技术并引入一些额外的算法。虽然本篇文章的第一部分涵盖决策树、支持向量机、逻辑回归以及合成分类随机森林,我们还是会添加 k-最近邻、朴素贝叶斯分类器和多层感知器。

Scikit-learn 分类器

k-最近邻(kNN)是一个简单分类器和懒惰学习者的示例,其中所有计算都发生在分类时间上(而不是提前在训练步骤期间发生)。kNN 是非参数的,通过比较数据实例和 k 最近实例来决定如何分类。

朴素贝叶斯是基于贝叶斯定理的分类器。它假定特征之间存在独立性,并且一个类中任何特定特征的存在与任何其它特征在同一类中的存在无关。

多层感知器(MLP)是一个简单的前馈神经网络,由多层节点组成,其中每个层与随后的层完全连接。多层感知器在 Scikit-learn 版本 0.18 中作了介绍。

首先从 Scikit-learn 文档中阅读 MLP 分类器的概述,然后使用教程练习实现。

第3步:更多聚类

我们现在接着讲聚类,一种无监督学习形式。上篇中,我们讨论了 k-means 算法; 我们在此介绍 DBSCAN 和期望最大化(EM)。

Scikit-learn聚类算法

首先,阅读这些介绍性文章; 第一个是 k 均值和 EM 聚类技术的快速比较,是对新聚类形式的一个很好的继续,第二个是对 Scikit-learn 中可用的聚类技术的概述:

期望最大化(EM)是概率聚类算法,并因此涉及确定实例属于特定聚类的概率。EM 接近统计模型中参数的最大似然性或最大后验估计(Han、Kamber 和 Pei)。EM 过程从一组参数开始迭代直到相对于 k 聚类的聚类最大化。

首先阅读关于 EM 算法的教程。接下来,看看相关的 Scikit-learn 文档。最后,按照教程使用 Python 自己实现 EM 聚类。

如果高斯混合模型初看起来令人困惑,那么来自 Scikit-learn 文档的这一相关部分应该可以减轻任何多余的担心:

高斯混合对象实现期望最大化(EM)算法以拟合高斯模型混合。

基于密度且具有噪声的空间聚类应用(DBSCAN)通过将密集数据点分组在一起,并将低密度数据点指定为异常值来进行操作。

首先从 Scikit-learn 的文档中阅读并遵循 DBSCAN 的示例实现,然后按照简明的教程学习:

第4步:更多的集成方法

上篇只涉及一个单一的集成方法:随机森林(RF)。RF 作为一个顶级的分类器,在过去几年中取得了巨大的成功,但它肯定不是唯一的集成分类器。我们将看看包装、提升和投票。

给我一个提升

首先,阅读这些集成学习器的概述,第一个是通用性的;第二个是它们与 Scikit-learn 有关:

然后,在继续使用新的集成方法之前,请通过一个新的教程快速学习随机森林:

包装、提升和投票都是不同形式的集成分类器,全部涉及建构多个模型; 然而,这些模型由什么算法构建,模型使用的数据,以及结果如何最终组合起来,这些都会随着方案而变化。

  • 包装:从同一分类算法构建多个模型,同时使用来自训练集的不同(独立)数据样本——Scikit-learn 实现包装分类器

  • 提升:从同一分类算法构建多个模型,一个接一个地链接模型,以提高每个后续模型的学习——Scikit-learn 实现 AdaBoost

  • 投票:构建来自不同分类算法的多个模型,并且使用标准来确定模型如何最好地组合——Scikit-learn 实现投票分类器

那么,为什么要组合模型?为了从一个特定角度处理这个问题,这里是偏差-方差权衡的概述,具体涉及到提升,以下是 Scikit-learn 文档:

  • 单一评估器 vs 包装:偏差-方差分解,Scikit-learn 文档。地址:suo.im/3izlRB

现在你已经阅读了关于集成学习器的一些介绍性材料,并且对几个特定的集成分类器有了基本了解,下面介绍如何从 Machine Learning Mastery 中使用 Scikit-learn 在 Python 中实现集成分类器:

第5步:梯度提升

下一步我们继续学习集成分类器,探讨一个当代最流行的机器学习算法。梯度提升最近在机器学习中产生了显著的影响,成为了 Kaggle 竞赛中最受欢迎和成功的算法之一。

给我一个梯度提升

首先,阅读梯度提升的概述:

接下来,了解为什么梯度提升是 Kaggle 竞赛中「最制胜」的方法:

虽然 Scikit-learn 有自己的梯度提升实现,我们将稍作改变,使用 XGBoost 库,我们提到过这是一个更快的实现。

以下链接提供了 XGBoost 库的一些额外信息,以及梯度提升(出于必要):

现在,按照这个教程把所有汇聚起来:

你还可以按照这些更简洁的示例进行强化:

第6步:更多的降维

降维是通过使用过程来获得一组主变量,将用于模型构建的变量从其初始数减少到一个减少数。

有两种主要形式的降维:

  • 1. 特征选择——选择相关特征的子集。地址:Feature selection

  • 2. 特征提取——构建一个信息性和非冗余的衍生值特征集。地址:Feature extraction

下面是一对常用的特征提取方法。

主成分分析(PCA)是一种统计步骤,它使用正交变换将可能相关变量的一组观测值转换为一组称为主成分的线性不相关变量值。主成分的数量小于或等于原始变量的数量。这种变换以这样的方式定义,即第一主成分具有最大可能的方差(即考虑数据中尽可能多的变率)

以上定义来自 PCA 维基百科条目,如果感兴趣可进一步阅读。但是,下面的概述/教程非常彻底:

线性判别分析(LDA)是 Fisher 线性判别的泛化,是统计学、模式识别和机器学习中使用的一种方法,用于发现线性组合特征或分离两个或多个类别的对象或事件的特征。所得到的组合可以用作线性分类器,或者更常见地,用作后续分类之前的降维。

LDA 与方差分析(ANOVA)和回归分析密切相关,它同样尝试将一个因变量表示为其他特征或测量的线性组合。然而,ANOVA 使用分类独立变量和连续因变量,而判别分析具有连续的独立变量和分类依赖变量(即类标签)。

上面的定义也来自维基百科。下面是完整的阅读:

你对 PCA 和 LDA 对于降维的实际差异是否感到困惑?Sebastian Raschka 做了如下澄清:

线性判别分析(LDA)和主成分分析(PCA)都是通常用于降维的线性转换技术。PCA 可以被描述为「无监督」算法,因为它「忽略」类标签,并且其目标是找到使数据集中的方差最大化的方向(所谓的主成分)。与 PCA 相反,LDA 是「监督的」并且计算表示使多个类之间的间隔最大化的轴的方向(「线性判别式」)。

有关这方面的简要说明,请阅读以下内容:

  • LDA 和 PCA 之间的降维有什么区别?作者 Sebastian Raschka。地址:Machine Learning FAQ

第 7 步:更多的深度学习

上篇中提供了一个学习神经网络和深度学习的入口。如果你的学习到目前比较顺利并希望巩固对神经网络的理解,并练习实现几个常见的神经网络模型,那么请继续往下看。

首先,看一些深度学习基础材料:

接下来,在 Google 的机器智能开源软件库 TensorFlow(一个有效的深度学习框架和现今几乎是最好的神经网络工具)尝试一些简明的概述/教程:

最后,直接从 TensorFlow 网站试用这些教程,它实现了一些最流行和常见的神经网络模型:

此外,目前一篇关于 7 个步骤掌握深度学习的文章正在写作之中,重点介绍使用位于 TensorFlow 顶部的高级 API,以增模型实现的容易性和灵活性。我也将在完成后在这儿添加一个链接。

相关的:

选自kdnuggets(1)(2) 机器之心编译

为何今年以来内存价格涨得这么疯狂?

$
0
0

  最近想配新电脑或者想给电脑升级内存的朋友应该发现了,从今年以来,内存价格一直在上涨。随着《绝地求生:大逃杀》的爆红,许多玩家都加入到了“吃鸡”大军中,虽然《绝地求生:大逃杀》所需配置不是很高,但内存是必须的。所以许多玩家都开始筹划着升级或者重新购置一台新电脑,但是看到当下内存的价格,都不惊叹出了一口气。


  在去年的时候,一条8G DDR4 2400hz的内存在300元左右,如今已经涨到了900多元,而即将被淘汰的DDR3内存也从250元飙升至500元,这可以说是前所未有的。要知道1年前,内存价格只占了整个机箱所有配置价格的5%到10%,如今的价格2条8G的内容就可以买到一块中高端显卡了。

  那么这到底是什么原因让内存涨得如此疯狂?从一份来自IC Insights的报告显示,内存的上游原料DRAM颗粒在这1年以来,涨幅到达111%,原料价格的上涨导致内存成本上升,是最大的原因。而DRAM颗粒价格疯涨的原因来自多方面的因素,整个状况估计在今年是无法改变的了。


  相信大家都知道,DRAM颗粒不但是电脑内存的原料,同时也是手机等电子产品内存的原料。而最近几年来,电脑市场基本饱和,电脑出货量已经连续5个季度持续下降,也导致了内存价格不断在下跌。在这个情况下,制造DRAM颗粒的厂商只能把销售方向转向了手机,为手机提供大量的货源。但是这个过程进行得并不顺利,手机需求的DRAM颗粒远远超出了预期,导致供货不足,这样一来,电脑内存和手机内存都无法满足。

  目前手机产品已经成为了生活必需品之一,手机市场已经变成了手机厂商们的战场,想要在这个战场中占得一席之地,就要不断的更新和推出新的产品,所以配置方面要不断的升级,内存的升级就是各大手机厂商的目标。目前主流的手机都配备了6GB的运行内存,而在明年,估计会升级到8GB,要知道这个已经是电脑的标准了,更何况手机的出货量远远高于电脑,到时DRAM颗粒会更加不足。


  另外,不少渠道商囤货待价格上涨之后再进行出售也导致了内存价格上涨。据了解,全球超过60%的内存条均由金士顿供应后,各大厂商的内存条出厂价格,每天都会根据金士顿当日的出货价格进行调整,而工厂方面出售的内存条价格涨幅是与上游DRAM颗粒的上涨幅度符合,而其中多出的价格很可能就是来自那些渠道商的定价了。

  当然除此之外,还有许多因素影响着内存的价格上涨。但如果中国能自己生产DRAM颗粒是最好的了,只可惜这么多年过去,国内DRAM产业一直都起不来,如今被别人牵着头走也没有办法。

阅读全文

关键词抽取算法的研究 | 吴良超的学习笔记

$
0
0

关键词抽取的一般步骤为:

分词–>过滤停止词,得到候选关键词–>从候选关键词中选出文章的关键词

从候选关键词中选出文章的关键词需要通过关键词抽取算法实现,而关键词抽取算法可以根据是否需要人工标注的语料进行训练而分为有监督的提取和无监督的提取。



有监督的提取需要人工标注的语料进行训练,人工预处理的代价较高。而无监督的抽取算法直接利用需要提取关键词的文本即可进行关键词的提取,因此适用性较强。

关键词抽取中无监督的抽取算法可分为三大类:

1)基于统计特征的,如TF-IDF

2)基于词图模型的,如TextRank

3)基于主题模型的,如LDA

本文主要讲述TF-IDF算法、TextRank算法、以及通过组合两者得到的三种新方法,然后通过Java实现这几种方法并比较这几种方法在特定语料库上进行关键词抽取的效果。

TF-IDF算法

TF-IDF(Term Frequency-Inverse Document Frequency)算法是一种基于统计特征的非常经典的算法,通过计算一个词的TF值和IDF值的乘积作为该值的得分,然后根据得分从大到小对词语排序,选择分数高的词语作为关键词。

TF值指词语在文本中出现的频率,如某篇文章分词并过滤停止词后的词语的数量为n,而其中的某个词语w出现的个数为m,则词w的TF值为

IDF值则指词语在整个语料库中的出现的频率大小。这里首先要指出的是TF-IDF算法是针对一个语料库(也就是多篇文档进行)进行关键词提取的算法。假如语料库中共有N篇文档,而出现了词语w的文档数为M。则词w的IDF值为

$TF(w)*IDF(w)$则为词w的TF-IDF值,根据这个值对候选词从大到小排序,选择前n个作为候选关键词即可。

通过Java的实现并不难,主要是利用Java的集合框架Map、List等存储词语的中间得分、以及候选关键词等。

实现的完整代码见:

https://github.com/WuLC/KeywordExtraction/blob/master/src/com/lc/nlp/keyword/algorithm/TFIDF.java

TextRank算法

TextRank算法是借鉴PageRank算法在语言处理中的一个算法,关于PageRank算法可参考这篇文章。无论是PageRank还是TextRank,其关键思想都是重要性传递

以PageRank为例,假如一个大型网站有一个超链接指向了某个小网站,那么小网站的重要性会上升,而上升的量则依据指向它的大网站的重要性。下图所示的就是一个例子:

假设网页A,B原来的重要性为100和9,那么根据他们指向的网页,传递给C、D的重要性分别为53和50。

在TextRank中将上图的网页替换成词语,将网页间的超链接换成词语间的语义关系;假如两个词的距离小于预设的距离,那么就认为这两个词间存在语义关系,否则不存在。这个预设的距离在TextRank算法中被称为同现窗口(co-occurance window)。这样便可构建出一个词的图模型。

但是在实际中应用时我们是无法预先知道网页A、B的重要性的,又或者说假如我们已经知道了网页的重要性,那么也不需要通过算法计算出网页的重要性了。这就成了一个先有鸡还是先有蛋的问题。

PageRank的原始论文提出了解决这个问题的方法,这篇文章中通过具体的例子提到了相关的理论依据,就是幂法求特征向量与初始值无关。具体做法就是,先给每个网页随机附一个初值,然后通过迭代计算直至收敛,理论证明了收敛的值与初始值无关。

同样的,TextRank也采取了相同的方法,就是先随机赋初值,然后通过迭代计算得到每个

词的重要性的得分。词语$V_i$的得分计算公式如下所示:

上式中各符号表示如下

实现的一个关键点在于构建词的图模型,在Java中通过队列实现,队列大小即为同现窗口的大小,移动队列的过程中将队列内部的词语互相连接。连接的形式通过java的Map<String,Set<String>>类型实现,表示指向词语(第一个String)的所有其他词语(Set<String>)的实现的关键代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

Map<String, Set<String>> words =newHashMap<String, Set<String>>();

Queue<String> que =newLinkedList<String>();

for(String w : wordList)//wordList为候选关键词

{

if(!words.containsKey(w))

{

words.put(w,newHashSet<String>());

}

que.offer(w);// 入队

if(que.size() > coOccuranceWindow)

{

que.poll();// 出队

}



for(String w1 : que)

{

for(String w2 : que)

{

if(w1.equals(w2))

{

continue;

}



words.get(w1).add(w2);

words.get(w2).add(w1);

}

}

}

另外一个实现关键点就是判断算法是否收敛,可以认为前后两次计算出来的值小于指定的阈值(一般取值较小,如0.000001)时算法收敛,或者超过设定的最大迭代次数时停止。实现的关键代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

min_diff =0.000001

Map<String, Float> score =newHashMap<String, Float>();

for(inti =0; i < max_iter; ++i)

{

Map<String, Float> m =newHashMap<String, Float>();

floatmax_diff =0;

for(Map.Entry<String, Set<String>> entry : words.entrySet())

{

String key = entry.getKey();

Set<String> value = entry.getValue();

m.put(key,1- d);

for(String other : value)

{

intsize = words.get(other).size();

if(key.equals(other) || size ==0)continue;

m.put(key, m.get(key) + d / size * (score.get(other) ==null?0: score.get(other)));

}



max_diff = Math.max(max_diff, Math.abs(m.get(key) - (score.get(key) ==null?1: score.get(key))));

}

score = m;



//exit once recurse

if(max_diff <= min_diff)

break;

}

完整的实现代码见:

https://github.com/WuLC/KeywordExtraction/blob/master/src/com/lc/nlp/keyword/algorithm/TextRank.java

综合TextRank 多同现窗口

由于TextRank的同现窗口的大小会影响提取的效果,如下图是同现窗口为2~10的时候评估值为F1值的变化情况。(测试语料

而原始的TextRank算法仅仅是建议该值设为2~10,无法知道对于一篇文章的最优同现窗口,因此本方法会综合TextRank多同现窗口的结果,将一个词语在不同大小的窗口下的得分相加,作为该词的总得分,然后根据总得分对词语排序,选择得分较高的前n个词作为候选关键词 。

该算法的效果与原始的TextRank算法的效果对比如下(测试语料

图中的textrank表示原始的TextRank算法的效果,而multi_window_textrank表示综合了大小为2~10的同现窗口的结果的效果。从图中可知,在提取关键词个数大于4个的时候,该方法的效果要优于原始的TextRank算法,但是F1值的提升幅度不大,并且实际运行的时候,综合多同现窗口的方法花费的时间是原始TextRank算法的14倍左右。

代码的具体实现见:

https://github.com/WuLC/KeywordExtraction/blob/master/src/com/lc/nlp/keyword/algorithm/TextRankWithMultiWin.java

TextRank 与 TF-IDF 综合

考虑词语的IDF值

由于TextRank算法仅考虑文档内部的结构信息,导致一些在各个文档的出现频率均较高且不属于停止词的词语最总的得分较高。原因是没有考虑词语在整个语料库中的权重。因此在TextRank算法得到的每个词的得分基础上,乘上这个词在整个语料库的IDF值,IDF值是TF-IDF算法中的一个概念,该值越大,表示这个词在语料库中出现的次数越少,越能代表该文档。

将词语的TextRank得分乘上这个词的IDF值后作为该词的新得分,然后根据得分从大到小排序,选择得分高的前n个词作为关键词即可。

下面是考虑了词语的IDF值的方法与原始的TextRank算法的效果对比图(测试语料

从图中可知,考虑了词语的IDF值后的方法的效果要优于原始的TextRank算法,运行时间约为TextRank算法的两倍。

完整的代码实现见:

https://github.com/WuLC/KeywordExtraction/blob/master/src/com/lc/nlp/keyword/algorithm/TextRankWithTFIDF.java

TextRank与TF-IDF投票

这种方法也是针对TextRank算法仅考虑文档内部的信息而忽略了文档外部的信息,综合TextRank算法和TF-IDF算法提取出来的结果。

具体的流程为:确定要抽取的关键词个数n,通过TextRank算法和TF-IDF算法对语料库分别提取2n个关键词,选择同时在两个算法得到的结果中出现的词语作为关键词,假如同时出现的词语不足n个,那么剩下的词语从TextRank的结果或TF-IDF的结果中补。

下面是TextRank和TF-IDF投票方法的结果与原始的TextRank算法的结果的对比图(测试语料

从结果可知,两个算法综合投票的方法的效果要优于原始的TextRank算法。运行的时间约为原始的TextRank的两倍。

完整的代码实现见:

https://github.com/WuLC/KeywordExtraction/blob/master/src/com/lc/nlp/keyword/algorithm/TextRankWithTFIDF.java

总结

本文主要讲述了TextRank算法以及对其进行简单改进的三种方法:综合多同现窗口的结果、考虑词语的IDF值、TF-IDF与TextRank共同投票。通过Java实现并比较其效果(评判指标为F1值)。下图是这几个算法的总效果对比图。(测试语料

综合多同现窗口的改进方案后的效果虽然要略优于原始的 TextRank 算法,但是消耗的时间是原始 TextRank 算法的 14 倍左右;综合 TextRank 算法和 TF-IDF 算法后的结果是改进算法后最优的,其次是考虑 TextRank 提取出的关键词的 IDF 值的改进方案,两者的效果均要优于原始的 TextRank 算法, 消耗的时间也比原始的 TextRank 算法要多。

因此,若需要对单篇文档提取的关键词时,可采用原始的TextRank算法或综合多同现窗口的方法, 假如对提取效果的要求较高且对时间要求不高时,可以采用综合多同现窗口的方法, 反之直接采用原始的TextRank算法。如果需要对多文档进行关键词抽取时,四种方法都可以采用,但是考虑提取的效果以及消耗的时间, 建议使用 TextRank 算法和 TF-IDF 算法综合投票的方法或 TextRank 结合IDF值的方法,并且根据着重点是时间还是提取的精度,选择 TF-IDF 算法综合投票的方法或 TextRank 结合 IDF 值的方法。

上文提到的所有代码的地址为:https://github.com/WuLC/KeywordExtraction

除了算法的实现,还包括了语料库的导入、F1值的计算方法的实现等。

德国科学家称人们天生就害怕蜘蛛和蛇

$
0
0

蜘蛛或蛇是让许多人毛骨悚然的动物。然而你是否曾经想过恐惧来自哪里?你可能会想象,你是对这些小动物的外形产生恐惧感。 但是新的研究已经彻底打破了这个观念,揭示了人们在能认出蜘蛛和蛇之前就已经对这些动物产生恐惧感。

3J15`CO6LMCY8GEHE_AZVLN.png

资料图

由德国马克斯-普朗克人类认知和脑科学研究所进行的这项研究发现,当面前出现蜘蛛或蛇的照片时,6个月大的婴儿产生压力反应,表明我们天生就害怕这些小动物。

~~7E]9KZGT$VFH@9IWG_(34.png

首席研究员Stefanie Hoehl表示:“当我们向婴儿展示一条蛇或一只蜘蛛的照片,而不是相同颜色的花朵或鱼类照片时,他们产生了更明显的瞳孔放大反应。在不变的光照条件下,瞳孔大小的这种变化是激活大脑中去甲肾上腺素能系统的重要信号,这是造成压力反应的原因。因此,即使最小的婴儿似乎也受到这些动物群体的压力。”

鉴于这个发现,研究人员认为人们对这些特定生物的恐惧具有进化的联系。然而,值得注意的是,对蜘蛛和蛇的这种恐惧倾向并不扩展到熊等许多其他危险的动物。

有人问什么是 5G 时,你除了说速度更快,还应该懂这些

$
0
0

5G 时代似乎突然离我们近了很多。

推出支持 5G 的 X50 调制解调器一年之后,前几天高通在香港的 4G/5G 峰会上 宣布在这款调制解调器上实现了第一个正式发布的 5G 连接,而爱立信的东北亚网络负责人 Luca Orsini 也表示最早在 2018 年下半年会开始部署 5G。

很快在你身边谈论 5G 的人会越来越多,是时候了解一下它了。

年初我曾经去到 澳大利亚体验了速度极快的千兆级网络,但这只是一个基础,高通会在 5G 上借用另外一样武器:毫米波。

毫米波属于高频电磁波,用它来传输数据,好处是速度极快,而且在同样的能耗下能传输更多的数据。

之前我们提到过,单单在千兆级 LTE 的网络下,我们打开在云端硬盘上存储的文件很容易就达到现在在本地打开的速度,而未来 5G 网络的最高速度将会是千兆级 LTE 的 5 倍,到时除了手机,5G 网络还会被运用在无人驾驶和远程医疗领域。

当然,每次网络迭代往更高的频段部署时都会遇到同一个问题:覆盖范围太小。因为频段越高,波长越短,传输范围越小。毫米波也不例外,如果信号传输时中间隔了一面墙,信号很可能就会被阻隔。这个原理就像太阳发射的光线没法穿透你家房子的墙壁一样。

高通产品市场高级总监 Peter Carson 表示,他们通过天线单元增益、波束成形等途径,成功让毫米波的信号增强(从刚发射出来时的 10dBm 增强到 30 dBm),最终实现即使在离基站 150 米的范围内,仍然能够维持千兆级的速率。

按高通的话讲,未来 5G 将会分为以下三种应用场景:增强型移动带宽(例如手机)、关键业务型服务(例如自动驾驶)以及海量物联网。

前两者都很好理解,毕竟都是依赖高频段高速率的网络,但在海量物联网上,一切却变得反其道而行之了。

在一些比较大型的基础设施(如街灯、建筑)中所使用的物联网组件,并不需要像手机一样的快速网络。对它们来说,低功耗、广泛的覆盖以及稳定的连接更为重要。这样他们能更好地和远处的部件交换数据,每次更换电池也许就可以用上几年或者更长的时间。

于是逐渐被人遗忘的窄带技术再次有了用武之地,这就是 LTE eMTC(Category M1)和 NB-IoT(Category NB-1)模式。

不同于处于高频率毫米波,这些信号以 1GHz 以下的低频率传输,具有超低功耗、长电池续航以及覆盖更广的特点。

这和 5G 有关系吗?当然有,5G 网络带来的并不是单一速度的提升,除了主打速度的毫米波,还有主打能耗比的窄带技术。Peter Carson 表示,5G 的窄带标准会在 5G 增强型移动宽带之后制定。

5G 是一个把高频段网络和低频段网络进行统一设计的大整合。但它真正到来之后,会对我们生活作出怎样的改变,似乎没人能说的清。

Peter Carson 做了这样的比喻:如果把 5G 比作电力,那我们提到的这些应用就像电灯泡一样,是电力的首个应用。

有意思的是,早在一年之前,这位读者在一篇文章中的评论我「如果有快到窒息的网速,你会用在哪些地方?」这句话的回答,几乎就是顺着 Peter 的比喻往下讲的:

和“如果电无限供应你会做什么?”一样只有在匮乏的时候才会提出的问题,在拨号上网时代,能够想象如今 24h 在线的移动互联网时代么?当 TB 级别互联网连接地球上每个终端的时候,现阶段人类的想象力还是太苍白!

还在想说什么好。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 |原文链接· 查看评论· 新浪微博


Lucene索引升级 - rainystars' Blog - SegmentFault

$
0
0

由于Lucene文件格式从2到3以及从3到4版本时都发生了重大的改变,造成了高版本无法读取低版本的数据,使用Lucene中的IndexUpgrader方法先将版本从2升到3,然后再从3升级到4。

import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexUpgrader;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
public class ConvertFromTwo {
    public static void main(String[] args) {
        ConvertFromTwo c = new ConvertFromTwo();
        c.upgrade();        
    }
    private final String INDEX_PATH = "C:/Users/rainystars/Desktop/index-synonym";  
    public void upgrade(){
        try {
            File dir = new File(INDEX_PATH);
            String path = "";
            String[] dirList = dir.list();
            for (String d:dirList){
                path = INDEX_PATH + "/" + d;
                IndexUpgrader t = new IndexUpgrader(FSDirectory.open(new File(path)),
        Version.LUCENE_36,null,true);
                t.upgrade();
                System.out.println(path+" Done.");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("All Done.");
    }
}

从版本2升级到版本3时,需要使用lucene3的jar包,我使用的lucene3.6的jar包,我需要处理的索引是在一个文件夹中所存在的一系列索引文件,所以需要循环来遍历每个目录。

import java.io.File;
import java.io.IOException;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexUpgrader;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
public class ConvertFromThree {
    public static void main(String[] args) {
        ConvertFromThree c = new ConvertFromThree();
        c.upgrade();        
    }
    private final String INDEX_PATH = "C:/Users/rainystars/Desktop/index-synonym";  
    public void upgrade(){
        try {
            File dir = new File(INDEX_PATH);
            String path = "";
            String[] dirList = dir.list();
            for (String d:dirList){
                path = INDEX_PATH + "/" + d;
                //System.out.println(path);
                IndexUpgrader t = new IndexUpgrader(FSDirectory.open(new File(path)),
                                                        Version.LUCENE_44,System.out,true);
                t.upgrade();
                System.out.println(path+" Done.");
            }       
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("All Done.");
    }
}

在3到4版本的转化中使用的是lucene4.4的jar包

中国新能源汽车温室气体排放量比内燃机汽车高50%_网易科技

$
0
0

(原标题:电动汽车:中国充满激情的权力游戏)

中国讨厌内燃发动机的理由有很多,并有巨大的动机加速其灭亡。它们很脏,中国政府称中国令人窒息的空气污染有约30%是拜内燃发动机所赐;使用内燃发动机导致中国大量从海外进口石油,而北京方面将石油进口视为一个重大战略弱点;内燃发动机还突显了中国的一个短板,这是中国国内汽车工业的一个长期缺陷――制造内燃发动机的水平不高。

中国新能源汽车温室气体排放量比内燃机汽车高50%

上个月中国政府极大地振奋了全球淘汰内燃机的运动。随着许多欧洲国家提出要在2025年到2040年间取缔传统燃油汽车,北京方面表示他们正在研究针对汽油和柴油汽车采取类似行动的时机。

此举受到环保人士欢迎,也符合中国国家规划部门的设想,他们认为中国在电动汽车市场上很有竞争力,甚至可以引领世界。

中国政府在9月份进一步坚定了立场,公布了一项稳步加码的配额制度,2019年起将奖励生产更多新能源汽车的汽车制造商,同时迫使汽车制造商为生产的每一辆传统汽车购买其他生产商的新能源汽车“积分”。

中国新能源汽车温室气体排放量比内燃机汽车高50%

作为全球最大也是利润最丰厚的汽车市场,中国在汽车业拥有巨大的影响力,并对利用这一影响力无所畏惧。中国拥有一种中央规划机制,其它一切考虑――如盈利能力及消费者喜好――都要服从政府的法令。中国在汽车行业已经倾注了数以十亿美元计的补贴和国家投资。

由于中国政府的这些干预,中国已成为全球最大的电动汽车生产国。中国汽车工业协会称,去年中国销售了507000辆电动汽车,包括公共汽车和商用汽车,约占世界电动汽车销售总量的45%。然而,中国政府设定了一个目标,到2025年电动汽车和混合动力汽车产量要达到700万量。

“他们可以下令在中国各地兴建电动汽车充电站,命令各大城市执行驾驶与牌照限制,”北京的一位西方汽车业高管说,他补充道,西方政府将很难效仿这些举措。

自20世纪60年代以来,对于亚洲的新兴经济体来说,挑选制胜的行业是一项已被反复证明有效的战略,中国致力于成为电动汽车行业的领导者,与日本和韩国的政策成功不谋而合。电动汽车只是中国雄心勃勃的“中国制造2025”行动纲领的一部分,这一纲领力求到下一个十年中期,将中国从一个低成本制造业国家,转变为一个在十大先进领域占优势的高科技强国――这些领域包括机器人技术、半导体及电动汽车。

如果说中国的环境与经济动机明确,其实中国还看到了一个可以利用的竞争优势:虽然中国的内燃机技术一直很落后,但中国有两家名列全球前5位的顶级锂电池制造商,宁德时代新能源科技公司与比亚迪。

“如果汽车的发动机与动力传动系统被一个简单的电池取代,全球汽车业巨头将会失去对汽车(生产)的控制,”上海咨询公司汽车市场预测的张豫说。“汽车变成了可以向任何人外包生产的一堆零件。”

简言之,汽车可能会成为继智能手机和电脑后,另一个由商品化硬件驱动的产业,这些硬件目前大部分在中国南方批量生产。当前,外国的竞争对手们封锁了混合动力与内燃机传动系统的技术,但众多中国企业在电池驱动技术上拥有专利优势。高盛估计,到2030年,中国将占据全球新能源汽车销售总量60%的份额。

“他们想要创造出一个(通过降低石油进口)满足本国安全需要、并且他们能够主导的产业,”前述汽车业高管称。

然而,近来中国的中央规划创新成果喜忧参半,项目往往更多地受到政治必要而非经济需求的驱动。即使成本高昂,但中国在高速铁路上的投资目前已在很大程度上取得了成功。而在多项欺诈指控导致32人被拘留后,中国为缓解交通拥堵而建设电动公交车的努力却在今年遭遇了滑铁卢。

中国新能源汽车温室气体排放量比内燃机汽车高50%

中国政府引导市场的许多努力都导致了产能过剩,因为企业为追逐政府补贴一拥而上。这在全球范围造成了从钢铁到太阳能电池板的供过于求。有些人担心电动汽车可能是下一个:近年来,超过200家企业宣布计划生产电动汽车。然而这个行业还要耗时多久、耗资多少才能变得在经济上可行,尚未可知。

这意味着,目前中国的电动汽车与混合动力汽车市场顽固地依赖政府补贴以维持竞争力。今年1月,当这些补贴下调了20%后,需求随即锐减。比亚迪E6是总部位于深圳的比亚迪2016年表现最好的纯电动汽车,2017年上半年,比亚迪E6的销量较去年同期下降了62%。总体看来,在补贴下降之后,今年1月至6月,比亚迪电动汽车和混合动力汽车的销量下滑了20%。

广汽集团董事冯兴亚今年6月在重庆举办的一个汽车论坛上表示,如果不考虑优惠政策或补贴,新能源汽车仍然无法与内燃机汽车竞争。

中国新能源汽车温室气体排放量比内燃机汽车高50%

北京大学光华管理学院的教授迈克尔?佩蒂斯说:“中国屡屡在一些最终无法持续的项目上亏钱。”成功的创新事迹反而来自私营部门,他说。像阿里巴巴和腾讯这样的互联网零售与社交网络公司“是悄悄崛起的”,实际上脱离了国家的视线,他补充道。

中国政府在这个行业的全部投资,包括各种补贴,规模非常之大。在2015年至2020年间,在新能源汽车补贴上,中国中央和地方政府预计将投入超过4000亿元人民币(合607亿美元,大致相当于乌兹别克斯坦的国内生产总值),然后补贴将在2021年逐步取消。这意味着去年售出的新能源汽车平均每辆有来自中国政府约10万元人民币(1.5万美元)的补贴。

至于中国国家电网对建设充电桩网络投资的250亿元人民币,中国政府的作用就越发突出了。中国官方新闻通讯社新华社称,中国全国已有17.1万个公共充电桩。但未来三年,这一数字预计还将大幅增长。目前有很多人抱怨难以找到充电桩。而根据美国能源部数据,美国只有44000个充电点和16000个充电站。

然而,大力推行电动汽车可能检验中国中央规划部门能力的局限性。据说中国的电池技术仍然落后于政府规划部门的雄心。专家称这些电池太过沉重也太过昂贵了,这意味着如果没有持续的补贴,电动汽车将无法吸引消费者。

中国新能源汽车温室气体排放量比内燃机汽车高50%

张豫说,很多消费者希望电动车续航能力达到400公里,而现有电池的功率密度仅能达到要求的一半左右。但指望电池技术领先于本身也变得越来越高效的汽油,取决于几个变量。

“如果没有技术上的突破,很难看到新能源汽车真正普及。问题是电池不适用于摩尔定律,”他说,他是指计算机处理器性能的指数级增长。

通用汽车首席执行官玛丽?巴拉上个月呼吁减少国家指令、更多地让市场力量决定中国电动汽车政策。她在上海发言时指出:“我认为让顾客选择满足他们需求的技术,而非政府下指令,才是最行之有效的做法。”

很多经济学家和电动汽车行业高管都在建议中国等待市场和电池技术赶上他们的雄心,但中国的政策制定者们坚持,国家对市场的干预是一种慷慨的帮助,这是让电动汽车业首先实现可持续发展所需的催化剂。

中国新能源汽车温室气体排放量比内燃机汽车高50%

根据配额制度,汽车制造商们每生产一辆电动或混合动力汽车将会获得一定积分,同时每生产一辆传统燃油汽车将会消耗一定积分。为顺应这一计划,大众、通用汽车、福特及其他汽车企业今年纷纷宣布成立合资企业,合资方主要是些规模较小的中国汽车业同行。

一名专家表示,它们生产的汽车主要是小型车,质量不好,为的只是完成配额。“这是一种很短视的做法――它们想先取得信任,帮助自己最大限度地通过政策获得财务好处,”美国管理咨询公司艾睿铂上海办事处的许谦说。

北京的一位行业游说人士表示,配额制度是把补贴的负担转移给私营部门、以取代政府逐步减少的补贴的一条途径。

中国新能源汽车温室气体排放量比内燃机汽车高50%

最大的障碍似乎于政府规划者考量中的一个新因素:消费者似乎不急着响应政府的号召。

今年4月,大众汽车(中国)首席执行官约赫姆?海兹曼表示,要卖出大量电动汽车将并非易事,但这是可运作的。他接着说,向汽车共享和打车平台车队出售产品,将在电动车业务中占据很大比例。“设计和生产这一产品是一方面,”他说,“另一方面,必须要有顾客购买。”

尽管中国所提供购车奖励的慷慨程度仅次于挪威,但这并未使电动车得到大范围普及。在挪威,混合动力车和电动车分别占到2016年新车购买量的24%和15%。而去年这两者仅占中国新车购买量的1.32%。发布汽车行业消费者数据的君迪的蔡明说,在很多地方,内燃机汽车悄悄地回来了。他说,车主们发现纯电动汽车很小,空间狭窄,于是又转回到内燃机汽车或插电式混合动力车。

惠誉评级表示,中国6个限制内燃机汽车牌照发放的城市便占到了电动汽车和混合动力汽车销量的70%。在北京,唯有通过摇号才能取得内燃机汽车的牌照,而在上海,牌照价格可能高达8万元人民币,而且要等待很长时间。许谦说,购买新能源汽车的人,主要想取得牌照,而不是电动车。

在中央计划的全盛期,中国政府拥有更大的决定权,可以不理会消费者想买什么,厂家想生产什么。现在情况不同了。新出现的中产阶层想要更宽敞、更舒适的驾驶体验。现实就是,除非或直到中国消费者想驾驶新能源车,这一市场也许永远不会起飞。

污染:燃煤发电削弱了电动车的清洁形象

从环境方面来看,支持发展新能源车的理由并不一目了然。

跟汽油车和柴油车相比,生产电动车并为其充入燃煤产生的电力,可能构成同样严重――也可能更严重――的污染问题,这只取决于怎么计算。

五名清华大学科学家5月发表在《应用能源》 的一篇文章显示,由于中国大约75%的电力是由煤炭产生的,而电池生产过程消耗大量能源,中国的新能源汽车产生的温室气体排放量实际上比内燃机汽车高出50%。

然而,根据麻省理工学院2007年的一项研究,直接比较来自大烟囱的污染和来自汽车排气管的污染并不恰当,因为汽车是在人们居住和生活的地方排放烟气的。该研究表明,从受影响的总人数和健康影响方面看,汽车排放的1吨有害颗粒(PM2.5)的危害大约是发电厂烟囱所排放1吨有害颗粒的11倍。

北京的绿色和平活动人士柳力说,考虑到电池的生产过程和燃煤发电,在中国新能源汽车的污染可能会超过内燃机汽车,因为这两者每行驶一公里所释放的二氧化碳和PM2.5水平大体相当。

但柳力表示,这种情况将发生改变――目前中国75%的电力来自煤炭,但到2030年可能会下降至50%以下。他接着说,监管电厂排放、让电厂更清洁也将变得更容易;由于燃料结构的变化,到2030年发电过程中的排放预计将减少三分之一。这还不包括电厂排放管控的改善,到2030年,这预计会把污染速度降低一半以上。

白鑫本文来源:FT中文网责任编辑:白鑫_NT4464

互联网架构,如何进行容量设计?

$
0
0

一,需求缘起

互联网公司,这样的场景是否似曾相识:

 

场景一:pm要做一个很大的运营活动,技术老大杀过来,问了两个问题:

1机器能抗住么?

2如果扛不住,需要加多少台机器?

 

场景二:系统设计阶段,技术老大杀过来,又问了两个问题:

1数据库需要分库么?

2如果需要分库,需要分几个库?

 

技术上来说,这些都是系统容量预估的问题,容量设计是架构师必备的技能之一。常见的容量评估包括数据量、并发量、带宽、CPU/MEM/DISK等,今天分享的内容,就以【并发量】为例,看看如何回答好这两个问题。

 

二,容量评估的步骤与方法

【步骤一:评估总访问量】

如何知道总访问量?对于一个运营活动的访问量评估,或者一个系统上线后PV的评估,有什么好的方法?

答案是:询问业务方,询问运营同学,询问产品同学,看对运营活动或者产品上线后的预期是什么。

 

举例:58要做一个APP-push的运营活动,计划在30分钟内完成5000w用户的push推送,预计push消息点击率10%,求push落地页系统的总访问量?

回答:5000w*10% = 500w

 

【步骤二:评估平均访问量QPS

如何知道平均访问量QPS

答案是:有了总量,除以总时间即可,如果按照天评估,一天按照4w秒计算

 

举例1push落地页系统30分钟的总访问量是500w,求平均访问量QPS

回答:500w/(30*60) = 2778,大概3000QPS

 

举例2:主站首页估计日均pv 8000w,求平均访问QPS

回答:一天按照4w秒算,8000w/4w=2000,大概2000QPS

 

提问:为什么一天按照4w秒计算?

回答:一天共24小时*60分钟*60=8w秒,一般假设所有请求都发生在白天,所以一般来说一天只按照4w秒评估

 

【步骤三:评估高峰QPS

系统容量规划时,不能只考虑平均QPS,而是要抗住高峰的QPS如何知道高峰QPS呢?

答案是:根据业务特性,通过业务访问曲线评估

 

举例:日均QPS2000,业务访问趋势图如下图,求峰值QPS预估



回答:从图中可以看出,峰值QPS大概是均值QPS2.5倍,日均QPS2000,于是评估出峰值QPS5000

 

说明:有一些业务例如“秒杀业务”比较难画出业务访问趋势图,这类业务的容量评估不在此列。

 

【步骤四:评估系统、单机极限QPS

如何评估一个业务,一个服务单机能的极限QPS呢?

答案是:压力测试

 

在一个服务上线前,一般来说是需要进行压力测试的(很多创业型公司,业务迭代很快的系统可能没有这一步,那就悲剧了),以APP-push运营活动落地页为例(日均QPS2000,峰值QPS5000),这个系统的架构可能是这样的:



1)访问端是APP

2)运营活动H5落地页是一个web站点

3H5落地页由缓存cache、数据库db中的数据拼装而成

 

通过压力测试发现,web层是瓶颈,tomcat压测单机只能抗住1200QPS(一般来说,1%的流量到数据库,数据库500QPS还是能轻松抗住的,cache的话QPS能抗住,需要评估cache的带宽,假设不是瓶颈),我们就得到了web单机极限的QPS1200。一般来说,线上系统是不会跑满到极限的,打个8折,单机线上允许跑到QPS1000

 

【步骤五:根据线上冗余度回答两个问题】

好了,上述步骤1-4已经得到了峰值QPS5000,单机QPS1000,假设线上部署了2台服务,就能自信自如的回答技术老大提出的问题了:

1)机器能抗住么?->峰值5000,单机1000,线上2台,扛不住

2)如果扛不住,需要加多少台机器?->需要额外3,提前预留1台更好,给4台更稳

 

除了并发量的容量预估,数据量、带宽、CPU/MEM/DISK等评估亦可遵循类似的步骤。

 

三,总结

互联网架构设计如何进行容量评估:

【步骤一:评估总访问量】->询问业务、产品、运营

【步骤二:评估平均访问量QPS->除以时间,一天算4w

【步骤三:评估高峰QPS->根据业务曲线图来

【步骤四:评估系统、单机极限QPS->压测很重要

【步骤五:根据线上冗余度回答两个问题】->估计冗余度与线上冗余度差值

 

个人一些经验分享,大伙轻拍,有更好的建议欢迎回复,下篇文章会将好的经验share给更多的同学。

==【完】==

回【无锁】如何实现超高并发的无锁缓存?

回【机房】从IDC到云端架构迁移之路

回【设置】线程数究竟设多少合理

回【单点】单点系统架构的可用性与性能优化

回【服务】互联网架构为什么要做服务化?




机器学习实战 MachineLearning/README.md at master · apachecn/MachineLearning · GitHub

$
0
0
  • ApacheCN - 学习机器学习群【629470233】ApacheCN - 学习机器学习群[629470233]
  • Machine Learning in Action (机器学习实战) |ApacheCN(apache中文网)
  • 电子版书籍:【机器学习实战-中文版-带目录版.pdf】
  • 视频每周更新:如果你觉得有价值,请帮忙点 Star【后续组织学习活动:sklearn + tensorflow】
  • --- 视频网站:优酷/bilibili / Acfun ,可直接在线播放。(最下方有相应链接)
  • --- 对于帮忙转发的朋友,可以私聊 企鹅 赠送《机器学习实战》百度云视频一套,谢谢

第一部分 分类

第二部分 利用回归预测数值型数据

第三部分 无监督学习

第四部分 其他工具

第五部分 项目实战(非课本内容)

阶段性总结

联系方式

项目负责人

项目贡献者

加入方式

网站视频

ApacheCN-机器学习视频-更新地址-AcFun 
ApacheCN-机器学习视频-更新地址-bilibili 
ApacheCN-机器学习视频-更新地址-优酷 

其它中文文档

国内首个机器人停车库 可一键存取车

$
0
0

近日,国内首个机器人停车库引起网友关注,在这个停车库里,停车、取车全都由机器人完成,车主只需原地站在出入口,就可以实现一键停车、取车。据悉,这座停车库位于南京地铁3号线夫子庙站,停车机器人长4.6米、宽2米,平均载重达2.5吨,行驶速度最高可达每秒1.5米,定位精准度的误差也在5毫米以内。

充满电后,可连续工作3个到4个小时。如果电量过低,它会自动跑到充电的地方去充电,2个小时就能充满。

它的主要任务是钻到车子下面,再把车驮到合适的停车位。机器人停车库的构造非常独特,地面一层是供车主停车、取车的停车平台,地下二层才是真正的停车场,机器人可以在其中自由穿梭。

据了解,该款停车机器人由深圳怡丰自动化科技有限公司研发,相比传统停车库,同样大的场地上,停车数量能增长40%以上,还可以做到全场无人,大大降低了安全隐患。

查看评论

如何理解微信订阅号变feed流这件事:向今日头条施压

$
0
0

根据最近几日的相关爆料和小道消息,微信订阅号呈现方式将迎来重大变化。

不多说,先看图。

是不是有些眼熟?

改版前,微信的订阅号的推送,是将所有公众号折叠起来,根据推送的时间顺序进行排列,未读的消息会有小红点和未读量提醒。点开公众号名片之后,才是一篇篇推送文章。打开文章至少需要3步。

而之后点击订阅号,直接变成一篇篇订阅的文章,不需要点击进入每一个公众号。如此,只要1步就可以完成。

换句话说,那就是在未来,订阅号可能会变成信息流。

微信订阅号为什么要改版?

最主要的原因是,现在微信公众号的流量已经被大号绑架了,平台的流动性,也就是从上到下的流通路径已经被堵死了。换句话说,微信公众号经过几年的发展,已经呈现出停滞的状态,这是微信公众号改革的前提和动因。

虽然,微信公众号的申请并没有停止增长,但是流量的分布却呈现出异常畸形的状态。头部的大号,如咪蒙等,牢牢地把控着公众号的流量;而众多其他公众号,即使有很好的内容,但由于错过了前几年的微信公众号增长红利期,并不能得到很好的发展,从而流量也十分有限。倒金字塔式的流量分布,稳定却难以再大幅增长。

简单看一下就知道:微信文章的阅读量来源,主要有三:订阅、朋友圈以及微信群。但这三个渠道面对如此庞大的微信公众号存量来说,还是太窄了。

改版的核心思路,就是将大号和小号同时拉到订阅号这个分发渠道里,以一种相对公平的方式去合理竞争,背后的目的,是让更多的小号能够提升打开率和阅读量,不至于在大号的压迫下,喘不过气来。

为什么要这么做,很简单,打个比方,如果你订阅了一大堆的大号,这些大号一天一推,内容又好,质量又高,在你的平均公众号阅读时间不提升的情况下(也就是总量不变的情况下),那些新出来的质量不错但推送和粉丝不如大号的小号们,显然难以被用户注意到,更别说会被主动传播和分发了。

订阅号打开率偏低是摆在微信团队一直以来非常重要的问题之一。在2017年微信第一季度发布的微信公众号数据来看,订阅号平均打开率约为4%,这个数据似乎并不能让微信团队满意。

所以,在现有的打开率下,即便是用户关注了,在大号的碾压下,多半也不会去点开,相当于流失了。

后面的两个渠道,虽然在文章的二次扩散上起到了很大的作用,但影响范围实在有限。微信的群聊像是一个个被组织起来的小群体,而朋友圈是进行社会化身份适应与自我形象建立的过程。在这两者的影响下,好友间彼此传播,内容、类别趋于一致,很难获取新的文章来源和文章品类,使得文章阅读并不能多次产生几何倍数的增长。

值得注意的是,前不久微信推出了“看一看”这一栏目,不得不说这一栏目的推广实在欠佳,甚至至今还有很多人不知道,或者没有使用这一栏目。

但如果细看,可以发现,“看一看”已经采用了类似今日头条feed流的模式,将文章根据订阅、推荐、好友在看的模式进行推广。这样通过推荐、订阅、阅读习惯加上强大的演算法操作,对文章推广来说意义重大,但在目前的微信体系内,并不太可能会得到大范围的推广和支持。

简而言之,如果将微信改版原因具象化,那么被堵住的浴缸将是很好的比喻。大量的内容如同马上要溢出的水,但出水口(订阅粉丝、朋友圈、群)却异常狭小。如何在水量不减的情况下,增加出水的能力和速度(即承载更多的内容、让更多的小号得到更多的阅读量与流量),这是微信团队应该思考的问题。

改版之后的微信能否疏通出水口,无论是否使用这种模式,都将是未来微信发展重要的节点。

改版之后会怎么样?

一、简化阅读步骤。也就是你不需要点开一个个公众号了。只需要点击一次,打开订阅号界面即可。

二、打破垄断。再一次将所有公众号拉到同一个起跑线上。这无疑会让以内容为王的自媒体获得新一轮发展和竞争。

当然,也可能也会出现以下的情况:

一、取关潮。大家都关注了很多几十条上百条未读的公众号,这些号平时“养在深闺人未识”现在一下子出现在你的信息流里,一些人可能会取关公众号,从而营造更加适合自己阅读的环境。

二、阅读量波动较大。大号的阅读量不会像以前那样,轻松十万+,而是会出现很大的波动。大号储存的粉丝资源优势,将在这一环境下很难的到有利发挥。

三、微信打击标题党。信息流自身所导致标题党盛行是很多人担忧的事情。但我认为,这一点不值得担心。从去年开始,各大自媒体平台对标题党的惩治措施,以及受众对标题党文章的免疫程度,已经使标题党失去的未来发展的空间。

四、保留旧版入口。改版后,势必会对一些公众号精准阅读的用户有一定影响。全部打散将文章放在一起,无疑会导致用户加大寻找时的时间成本。这一点,微信团队应该有预料,我的猜测,微信在改版的过程中,或许会多保留一个选项,也就是开放新版旧版给用户,让他们自己决定,方便精准阅读的用户。

为什么说是对头条施压

我们知道,今日头条的主要收入,是广告收入,也就是原来百度搜索的蛋糕,头条依靠海量的内容,精准的算法推荐,以及大量的销售人员(据说头条员工中很多是销售),赚了很多钱,今年头条的销售目标应该在100亿到160亿之间,这已经给百度造成了很大的压力。

头条现在的处境很麻烦,抢了百度的生意所以是百度的敌人,抢了微博的大客户(京东)所以是微博的敌人,抢了腾讯的广告业务所以是腾讯的敌人,之前传出百度收购头条的绯闻,结果头条方面予以强力否认,要么坚持独立发展,要么投靠BAT。

我相信BAT三家私底下应该都跟头条接触过投资的事情,现在腾讯上线feed流,相当于在谈判桌上又加了一个筹码,因为订阅号变feed流,接下来加上信息流广告是轻而易举的事情,以微信9亿月活的体量,头条的用户基数再大,也没办法跟订阅号这个流量黑洞比。

这就好比两个国家在谈判,一个国家在边境上部署了一百万军队待命,随时可以攻击,就问你怕不怕,要是不怂就冒着开战的风险硬抗,要是怂了只能在谈判桌上投降。

如果微信上线了订阅号信息流,相当于直接在头条家门口部署了百万军队,逼迫你认怂,因为这个信号已经非常明显了,微信一旦拥有“看一看”和“订阅号信息流”这两个武器,那么理论上就可以发动对于头条的直接袭击,并且可以把头条打的毫无还手之力。

我不清楚头条和微信是否正在资本的谈判桌上激烈的角力,但我认为官方如此“不小心”释放出这个信息,投石问路的意味要更深,而且,这个石头,是投向头条这边的。

@今日话题 



本话题在雪球有2条讨论,点击查看。
雪球是一个投资者的社交网络,聪明的投资者都在这里。
点击下载雪球手机客户端 http://xueqiu.com/xz

干货 | 精选《SQL注入、渗透、反病毒》学习总结集锦给你们~

$
0
0

本次 “开学季拜师活动”的徒弟们在师父的精心指导下,在短短5天内得到了迅速地成长,以前或当时遇到的问题都能够柳暗花明,技能稳步加血!虽不能说都是技术大作但都是认真学习的经验心得总结,字字真心,发自肺腑!(第一期拜师活动已结束,戳我回顾哦~ )

1111.png

他们遇到的坑可能你曾经或现在也在纠结中,不妨学习学,生活可能会欺骗你,但是你吃过的饭和你看过的书挖过的洞一定不会骗你!

我们从 50多篇学成总结中,根据不同知识点及各(ji)个(shu)门(fang)派(xiang)给大家 推荐了15篇优质技术帖,供大家免费学习哟~  

同时@那些小伙伴们,错过了活动不要再错过了这篇干货啦! 

 
以下《优质学习总结》点击文字即可查看 ↓     (排名不分先后,选出的帖子都是有代表性的哦~)


1、渗透测试>>>【脑洞大开】WIFI密码窃取

2、SQL注入>>>我的sql注入学习笔记   

3、SQL注入>>>SQL简单注入(入门级)
      
4、反病毒>>>我是这样进行简单的木马APP分析的(Android Killer分析)

5、反病毒>>>另类木马APP分析

6、渗透测试>>>渗透之从新手入门到入狱

7、渗透测试>>>渗透测试环境搭建(欢迎讨论)   

8、反病毒>>>LOL王者辅助病毒粗浅分析  

9、web安全>>>命令执行之盲注(技巧篇)

10、web安全>>>命令注入盲注的一些基础

11、web安全>>>命令盲注思路分享

12、web安全>>>寻找命令盲注的最佳姿势

13、反病毒>>>熊孩子(恶作剧)病毒分析-及专杀工具(含附件)

14、反病毒>>>熊孩子病毒简单分析及专杀工具编写(详解)

15、web安全>>>菜鸟初识命令注入&思路分享

自春秋时代始,老师这个词汇第一次走进了人们的生活。
何为师? 师者传道解惑,教人以道者之称也!
道,可为道理,也可为知识。
师徒制传承几千年至今仍为世界主流的教育制度,人们看重的就是其超高的效率,由一代人的经验所提炼的精华,传承给另一代人,知识不断的提炼和积累,造就了如今非凡的人类文化。
故新人总是感觉到入门的困难,充满了对未知的胆怯,因为知识本身是死的,但人为的教导却可以让知识活起来,只要能帮助你们踏出学习的第一步,你的未来就将彻底改变!
而这恰恰切合了i春秋学院的育人精神,也是我们第一次尝试举办拜师活动的初衷!

用户画像之标签权重算法

$
0
0

image.png

感谢大家长期以来对专栏的关注,最近工作比较忙,好久没更新了。接下来的几篇文章想和大家分享下关于用户画像的一些东西。今天我们先从用户画像的标签权重开始聊起吧。

用户画像:即用户信息标签化,通过收集用户社会属性、消费习惯、偏好特征等各个维度数据,进而对用户或者产品特征属性的刻画,并对这些特征分析统计挖掘潜在价值信息,从而抽象出一个用户的信息全貌,可看做是企业应用大数据的根基,是定向广告投放与个性化推荐的前置条件。

先举个场景,程序员小Z在某电商平台上注册了账号,经过一段时间在该电商平台的web端/app端进行浏览、所搜、收藏商品、下单购物等系列行为,该电商平台数据库已全程记录该用户在平台上的行为,通过系列建模算法,给程序员小Z打上了符合其特征的标签(如下图所示)。此后程序员小Z在该电商平台的相关推荐版块上总能发现自己想买的商品,总能在下单前犹豫不决时收到优惠券的推送,总是在平台上越逛越喜欢....

image.png

上面的例子是用户画像一些应用场景。而本文主要分享的是打在用户身上标签的权重是如何确定的。

image.png



如上图所示,一个用户标签表里面包括常见的字段如:用户id、用户姓名、标签id、标签名称、用户与该标签发生行为的次数(如搜索了两次“大数据”这个关键词)、行为类型(不同的行为类型对应用户对商品不同的意愿强度,如购买某商品>收藏某商品>浏览某商品>搜索某商品),行为时间(越久远的时间对用户当前的影响越小,如5年前你会搜索一本高考的书,而现在你会搜索一本考研的书)。最后非常重要的一个字段是标签权重,该权重影响着对用户属性的归类,属性归类不准确,接下来基于画像对用户进行推荐、营销的准确性也就无从谈起了。下面我们来讲两种权重的划分方法:

1、基于TF-IDF算法的权重归类

TF-IDF算法是什么思想,这里不做详细展开,简而言之:一个词语的重要性随着它在该文章出现的次数成正比,随它在整个文档集中出现的次数成反比。

image.png

比如说我们这里有3个用户和4个标签,标签和用户之间的关系将会在一定程度上反应出标签之间的关系。这里我们用w(P , T)表示一个标签T被用于标记用户P的次数。TF(P , T)表示这个标记次数在用户P所有标签中所占的比重,公式如下图:

image.png

对上面的图来说,用户1身上打了标签A 5个,标签B 2个,标签C 1个,那么用户1身上的A标签TF=5/(5+2+1) 。 相应的IDF(P , T)表示标签T在全部标签中的稀缺程度,即这个标签的出现几率。如果一个标签T出现几率很小,并且同时被用于标记某用户,这就使得该用户与该标签T之间的关系更加紧密。

image.png

然后我们根据TF * IDF即可得到该用户该标签的权重值。到这里还没结束,此时的权重是不考虑业务场景,仅考虑用户与标签之间的关系,显然是不够的。还需要考虑到该标签所处的业务场景、发生的时间距今多久、用户产生该标签的行为次数等等因素。我用个图总结下:

image.png

2、基于相关系数矩阵的权重归类

这个相关系数矩阵听title挺困难,其实道理十分简单。举个例子:用户1身上打上了5个A标签、2个B标签、1个C标签;用户2身上打上了4个A标签,3个B标签;用户3身上打上了4个C标签、1个D标签。

用个图形象表示一下:

image.png

那么同时打上A、B标签的用户有两个人,这就说明AB之间可能存在某种相关性,当用户量、标签量级越多时,标签两两之间的相关性也越明显。

今天先聊这么多,大家可以留言交流。后面再更新 ...

    聊天机器人学习 | 李强的博客

    $
    0
    0

    原文

    涉及知识

    人工智能一直以来是人类的梦想,造一台可以为你做一切事情并且有情感的机器人,像哆啦A梦一样,现在这已经不是一个梦了:iPhone里会说话的siri、会下棋的阿法狗、小度机器人、大白……,他们都能够具有智能,和人类交互,帮人类解决问题,这听起来非常神奇,实际上我们自己也可以做一个这样的机器人,从今天开始分享我将我学习和制作的过程

    智能机器人可以做到的事情可以很复杂:文字、语音、视频识别与合成;自然语言理解、人机对话;以及驱动硬件设备形成的“机器”人。作为一个只有技术和时间而没有金钱的IT人士,我仅做自然语言和人工智能相关的内容,不涉及硬件,也不涉及不擅长的多媒体识别和合成。所以索性就做一个可以和你说话,帮你解决问题的聊天机器人吧。

    聊天机器人涉及到的知识主要是自然语言处理,当然这包括了:语言分析和理解、语言生成、机器学习、人机对话、信息检索、信息传输与信息存储、文本分类、自动文摘、数学方法、语言资源、系统评测等内容,同时少不了的是支撑着一切的编程技术

    在我的桌上摆了很多有关自然语言处理、机器学习、深度学习、数学等方面的书籍,为了和大家分享我的经历、学到的知识和每一阶段的成果,我每天会花两个小时以上时间或翻书或总结或编码或整理或写文章,或许文章几天才能更新一篇,但我希望每一篇都是有价值的,或许文章里的知识讲解的不是非常深入,但我希望可以为你指明方向,对于晦涩难懂的内容,我尽量用简朴幽默的方式说出来,目的就是让每一位读者都能有收获,并朝着我们的目标一起前进。

    初识NLTK库

    安装和使用

    安装

    1
    pip install nltk

    下载数据

    1
    2
    importnltk
    nltk.download()

    选择book下载,下载较慢,推荐找网络资源。

    使用

    1
    from nltk.book import *

    你会看到可以正常加载书籍如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    *** Introductory Examples for the NLTK Book ***
    Loading text1, ..., text9 and sent1, ..., sent9
    Type the name of the text or sentence to view it.
    Type: 'texts()' or 'sents()' to list the materials.
    text1: Moby Dick by Herman Melville 1851
    text2: Sense and Sensibility by Jane Austen 1811
    text3: The Book of Genesis
    text4: Inaugural Address Corpus
    text5: Chat Corpus
    text6: Monty Python and the Holy Grail
    text7: Wall Street Journal
    text8: Personals Corpus
    text9: The Man Who Was Thursday by G . K . Chesterton 1908

    这里面的text*都是一个一个的书籍节点,直接输入text1会输出书籍标题:

    1
    text1
    1
    <Text: Moby Dick by Herman Melville 1851>

    搜索文本

    执行

    1
    text1.concordance("former")

    会显示20个包含former的语句上下文

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    Displaying 20 of 20 matches:
    s of the sea , appeared . Among the former , one was of a most monstrous size
    ce , borrowed from the chaplain ' s former sea - farings . Between the marble
    s him with a fresh lance , when the former one has been badly twisted , or elb
    , though smaller than those of the former order , nevertheless retain a propo
    fficial is still retained , but his former dignity is sadly abridged . At pres
    tested reality of his might had in former legendary times thrown its shadow b
    g associated with the experience of former perils ; for what knows he , this N
    ns and places in which , on various former voyages of various ships , sperm wh
    . So that though Moby Dick had in a former year been seen , for example , on w
    ed by the defection of seven of his former associates , and stung by the mocki
    no part in the mutiny , he told the former that he had a good mind to flog the
    so for ever got the start of their former captain , had he been at all minded
    head is cut off whole , but in the former the lips and tongue are separately
    nd the right . While the ear of the former has an external opening , that of t
    in small detached companies , as in former times , are now frequently met with
    ence on the coast of Greenland , in former times , of a Dutch village called S
    x months before he wheeled out of a former equinox at Aries ! From storm to st
    Sperm Whale , for example , that in former years ( the latter part of the last
    les no longer haunt many grounds in former years abounding with them , hence t
    ering was but the direct issue of a former woe ; and he too plainly seemed to

    我们还可以搜索相关词,比如:

    1
    text1.similar("ship")
    1
    2
    whale boat sea captain world way head time crew man other pequod line
    deck body fishery air boats side voyage

    输入了ship,查找了boat,都是近义词

    我们还可以查看某个词在文章里出现的位置:

    1
    text4.dispersion_plot(["citizens","democracy","freedom","duties","America"])

    词统计

    len(text1):返回总字数

    set(text1):返回文本的所有词集合

    len(set(text4)):返回文本总词数

    text4.count(“is”):返回“is”这个词出现的总次数

    FreqDist(text1):统计文章的词频并按从大到小排序存到一个列表里

    fdist1 = FreqDist(text1);fdist1.plot(50, cumulative=True):统计词频,并输出累计图像

    纵轴表示累加了横轴里的词之后总词数是多少,这样看来,这些词加起来几乎达到了文章的总词数

    fdist1.hapaxes():返回只出现一次的词

    text4.collocations():频繁的双联词

    自然语言处理关键点

    词意理解:中国队大胜美国队;中国队大败美国队。“胜”、“败”一对反义词,却表达同样的意思:中国赢了,美国输了。这需要机器能够自动分析出谁胜谁负

    自动生成语言:自动生成语言基于语言的自动理解,不理解就无法自动生成

    机器翻译:现在机器翻译已经很多了,但是还很难达到最佳,比如我们把中文翻译成英文,再翻译成中文,再翻译成英文,来回10轮,发现和最初差别还是非常大的。

    人机对话:这也是我们想做到的最终目标,这里有一个叫做“图灵测试”的方式,也就是在5分钟之内回答提出问题的30%即通过,能通过则认为有智能了。

    自然语言处理分两派,一派是基于规则的,也就是完全从语法句法等出发,按照语言的规则来分析和处理,这在上个世纪经历了很多年的试验宣告失败,因为规则太多太多,而且很多语言都不按套路出牌,想象你追赶你的影子,你跑的快他跑的更快,你永远都追不上它。另一派是基于统计的,也就是收集大量的语料数据,通过统计学习的方式来理解语言,这在当代越来越受重视而且已经成为趋势,因为随着硬件技术的发展,大数据存储和计算已经不是问题,无论有什么样的规则,语言都是有统计规律的,当然基于统计也存在缺陷,那就是“小概率事件总是不会发生的”导致总有一些问题解决不了。

    下一节我们就基于统计的方案来解决语料的问题。

    语料与词汇资源

    当代自然语言处理都是基于统计的,统计自然需要很多样本,因此语料和词汇资源是必不可少的,本节介绍语料和词汇资源的重要性和获取方式

    NLTK语料库

    NLTK包含多种语料库,举一个例子:Gutenberg语料库,执行:

    1
    2
    importnltk
    nltk.corpus.gutenberg.fileids()

    返回Gutenberg语料库的文件标识符

    1
    [u'austen-emma.txt', u'austen-persuasion.txt', u'austen-sense.txt', u'bible-kjv.txt', u'blake-poems.txt', u'bryant-stories.txt', u'burgess-busterbrown.txt', u'carroll-alice.txt', u'chesterton-ball.txt', u'chesterton-brown.txt', u'chesterton-thursday.txt', u'edgeworth-parents.txt', u'melville-moby_dick.txt', u'milton-paradise.txt', u'shakespeare-caesar.txt', u'shakespeare-hamlet.txt', u'shakespeare-macbeth.txt', u'whitman-leaves.txt']

    nltk.corpus.gutenberg就是gutenberg语料库的阅读器,它有很多实用的方法,比如:

    nltk.corpus.gutenberg.raw('chesterton-brown.txt'):输出chesterton-brown.txt文章的原始内容

    nltk.corpus.gutenberg.words('chesterton-brown.txt'):输出chesterton-brown.txt文章的单词列表

    nltk.corpus.gutenberg.sents('chesterton-brown.txt'):输出chesterton-brown.txt文章的句子列表

    类似的语料库还有:

    from nltk.corpus import webtext:网络文本语料库,网络和聊天文本

    from nltk.corpus import brown:布朗语料库,按照文本分类好的500个不同来源的文本

    from nltk.corpus import reuters:路透社语料库,1万多个新闻文档

    from nltk.corpus import inaugural:就职演说语料库,55个总统的演说

    语料库的一般结构

    以上各种语料库都是分别建立的,因此会稍有一些区别,但是不外乎以下几种组织结构:散养式(孤立的多篇文章)、分类式(按照类别组织,相互之间没有交集)、交叉式(一篇文章可能属于多个类)、渐变式(语法随着时间发生变化)

    语料库的通用接口

    fileids():返回语料库中的文件

    categories():返回语料库中的分类

    raw():返回语料库的原始内容

    words():返回语料库中的词汇

    sents():返回语料库句子

    abspath():指定文件在磁盘上的位置

    open():打开语料库的文件流

    加载自己的语料库

    收集自己的语料文件(文本文件)到某路径下(比如/tmp),然后执行:

    1
    2
    3
    4
    from nltk.corpus import PlaintextCorpusReader
    corpus_root = '/tmp'
    wordlists = PlaintextCorpusReader(corpus_root, '.*')
    wordlists.fileids()

    就可以列出自己语料库的各个文件了,也可以使用如wordlists.sents('a.txt')wordlists.words('a.txt')等方法来获取句子和词信息

    条件频率分布

    条件分布大家都比较熟悉了,就是在一定条件下某个事件的概率分布。自然语言的条件频率分布就是指定条件下某个事件的频率分布。

    比如要输出在布朗语料库中每个类别条件下每个词的概率:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # coding:utf-8
    import sys
    reload(sys)
    sys.setdefaultencoding( "utf-8" )
    import nltk
    from nltk.corpus import brown
    # 链表推导式,genre是brown语料库里的所有类别列表,word是这个类别中的词汇列表
    # (genre, word)就是类别加词汇对
    genre_word = [(genre, word)
    for genre in brown.categories()
    for word in brown.words(categories=genre)
    ]
    # 创建条件频率分布
    cfd = nltk.ConditionalFreqDist(genre_word)
    # 指定条件和样本作图
    cfd.plot(conditions=['news','adventure'], samples=[u'stock', u'sunbonnet', u'Elevated', u'narcotic', u'four', u'woods', u'railing', u'Until', u'aggression', u'marching', u'looking', u'eligible', u'electricity', u'$25-a-plate', u'consulate', u'Casey', u'all-county', u'Belgians', u'Western', u'1959-60', u'Duhagon', u'sinking', u'1,119', u'co-operation', u'Famed', u'regional', u'Charitable', u'appropriation', u'yellow', u'uncertain', u'Heights', u'bringing', u'prize', u'Loen', u'Publique', u'wooden', u'Loeb', u'963', u'specialties', u'Sands', u'succession', u'Paul', u'Phyfe'])

    注意:这里如果把plot直接换成tabulate ,那么就是输出表格形式,和图像表达的意思相同

    我们还可以利用条件频率分布,按照最大条件概率生成双连词,最终生成一个随机文本

    这可以直接使用bigrams()函数,它的功能是生成词对链表。

    创建python文件如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    # coding:utf-8
    import sys
    reload(sys)
    sys.setdefaultencoding( "utf-8" )
    import nltk
    # 循环10次,从cfdist中取当前单词最大概率的连词,并打印出来
    def generate_model(cfdist, word, num=10):
    for i in range(num):
    print word,
    word = cfdist[word].max()
    # 加载语料库
    text = nltk.corpus.genesis.words('english-kjv.txt')
    # 生成双连词
    bigrams = nltk.bigrams(text)
    # 生成条件频率分布
    cfd = nltk.ConditionalFreqDist(bigrams)
    # 以the开头,生成随机串
    generate_model(cfd, 'the')

    执行效果如下:

    1
    the land of the land of the land of the

    the的最大概率的双连词是land,land最大概率双连词是of,of最大概率双连词是the,所以后面就循环了

    其他词典资源

    有一些仅是词或短语以及一些相关信息的集合,叫做词典资源。

    词汇列表语料库:nltk.corpus.words.words(),所有英文单词,这个可以用来识别语法错误

    停用词语料库:nltk.corpus.stopwords.words,用来识别那些最频繁出现的没有意义的词

    发音词典:nltk.corpus.cmudict.dict(),用来输出每个英文单词的发音

    比较词表:nltk.corpus.swadesh,多种语言核心200多个词的对照,可以作为语言翻译的基础

    同义词集:WordNet,面向语义的英语词典,由同义词集组成,并组织成一个网络

    何须动手?完全自动化对语料做词性标注

    全人工对语料做词性标注就像蚂蚁一样忙忙碌碌,是非常耗费声明的,如果有一个机器能够完全自动化地,给它一篇语料,它迅速给你一片标注,这样才甚好,本节就来讨论一下怎么样能无需动手对语料做自动化的词性标注

    英文词干提取器

    1
    2
    3
    importnltk
    porter = nltk.PorterStemmer()
    printporter.stem('lying')

    输出lie

    词性标注器

    1
    2
    3
    importnltk
    text = nltk.word_tokenize("And now for something completely different")
    printnltk.pos_tag(text)
    1
    [('And', 'CC'), ('now', 'RB'), ('for', 'IN'), ('something', 'NN'), ('completely', 'RB'), ('different', 'JJ')]

    其中CC是连接词,RB是副词,IN是介词,NN是名次,JJ是形容词

    这是一句完整的话,实际上pos_tag是处理一个词序列,会根据句子来动态判断,比如:

    1
    printnltk.pos_tag(['i','love','you'])
    1
    [('i', 'NN'), ('love', 'VBP'), ('you', 'PRP')]`

    这里的love识别为动词

    而:

    1
    print nltk.pos_tag(['love','and','hate'])
    1
    [('love', 'NN'), ('and', 'CC'), ('hate', 'NN')]

    这里的love识别为名词

    nltk中多数都是英文的词性标注语料库,如果我们想自己标注一批语料库该怎么办呢?

    nltk提供了比较方便的方法:

    1
    2
    tagged_token = nltk.tag.str2tuple('fly/NN')
    printtagged_token
    1
    ('fly', 'NN')

    这里的nltk.tag.str2tuple可以把fly/NN这种字符串转成一个二元组,事实上nltk的语料库中都是这种字符串形式的标注,那么我们如果把语料库标记成:

    1
    2
    sent ='我/NN 是/IN 一个/AT 大/JJ 傻×/NN'
    print[nltk.tag.str2tuple(t)fortinsent.split()]
    1
    [('\xe6\x88\x91', 'NN'), ('\xe6\x98\xaf', 'IN'), ('\xe4\xb8\x80\xe4\xb8\xaa', 'AT'), ('\xe5\xa4\xa7', 'JJ'), ('\xe5\x82\xbb\xc3\x97', 'NN')]

    这么说来,中文也是可以支持的,恩~

    我们来看一下布朗语料库中的标注:

    1
    nltk.corpus.brown.tagged_words()
    1
    [(u'The', u'AT'), (u'Fulton', u'NP-TL'), ...]

    事实上nltk也有中文的语料库,我们来下载下来:

    执行nltk.download(),选择Corpora里的sinica_treebank下载

    sinica就是台湾话中的中国研究院

    我们看一下这个中文语料库里有什么内容,创建cn_tag.py,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importnltk
    forwordinnltk.corpus.sinica_treebank.tagged_words():
    printword[0], word[1]

    执行后输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    一 Neu
    友情 Nad
    嘉珍 Nba
    和 Caa
    我 Nhaa
    住在 VC1
    同一條 DM
    巷子 Nab
    我們 Nhaa
    是 V_11
    ……

    第一列是中文的词汇,第二列是标注好的词性

    我们发现这里面都是繁体,因为是基于台湾的语料生成的,想要简体中文还得自己想办法。不过有人已经帮我们做了这部分工作,那就是jieba切词,https://github.com/fxsjy/jieba,强烈推荐,可以自己加载自己的语料,进行中文切词,并且能够自动做词性标注

    词性自动标注

    面对一片新的语料库(比如我们从未处理过中文,只有一批批的中文语料,现在让我们做词性自动标注),如何实现词性自动标注?有如下几种标注方法:

    默认标注器:不管什么词,都标注为频率最高的一种词性。比如经过分析,所有中文语料里的词是名次的概率是13%最大,那么我们的默认标注器就全部标注为名次。这种标注器一般作为其他标注器处理之后的最后一道门,即:不知道是什么词?那么他是名次。默认标注器用DefaultTagger来实现,具体用法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importnltk
    default_tagger = nltk.DefaultTagger('NN')
    raw ='我 累 个 去'
    tokens = nltk.word_tokenize(raw)
    tags = default_tagger.tag(tokens)
    printtags
    1
    [('\xe6\x88\x91', 'NN'), ('\xe7\xb4\xaf', 'NN'), ('\xe4\xb8\xaa', 'NN'), ('\xe5\x8e', 'NN'), ('\xbb', 'NN')]

    正则表达式标注器:满足特定正则表达式的认为是某种词性,比如凡是带“们”的都认为是代词(PRO)。正则表达式标注器通RegexpTagge实现,用法如下:

    1
    2
    3
    pattern = [(r'.*们$','PRO')]
    tagger = nltk.RegexpTagger(pattern)
    printtagger.tag(nltk.word_tokenize('我们 累 个 去 你们 和 他们 啊'))
    1
    [('\xe6\x88\x91\xe4', None), ('\xbb', None), ('\xac', None), ('\xe7\xb4\xaf', None), ('\xe4\xb8\xaa', None), ('\xe5\x8e', None), ('\xbb', None), ('\xe4\xbd\xa0\xe4', None), ('\xbb', None), ('\xac', None), ('\xe5\x92\x8c', None), ('\xe4', None), ('\xbb', None), ('\x96\xe4', None), ('\xbb', None), ('\xac', None), ('\xe5\x95\x8a', None)]

    查询标注器:找出最频繁的n个词以及它的词性,然后用这个信息去查找语料库,匹配的就标记上,剩余的词使用默认标注器(回退)。这一般使用一元标注的方式,见下面。

    一元标注:基于已经标注的语料库做训练,然后用训练好的模型来标注新的语料,使用方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    importnltk
    fromnltk.corpusimportbrown
    tagged_sents = [[(u'我',u'PRO'), (u'小兔',u'NN')]]
    unigram_tagger = nltk.UnigramTagger(tagged_sents)
    sents = brown.sents(categories='news')
    sents = [[u'我',u'你',u'小兔']]
    tags = unigram_tagger.tag(sents[0])
    printtags
    1
    [(u'\u6211', u'PRO'), (u'\u4f60', None), (u'\u5c0f\u5154', u'NN')]

    这里的tagged_sents是用于训练的语料库,我们也可以直接用已有的标注好的语料库,比如:

    1
    brown_tagged_sents = brown.tagged_sents(categories='news')

    二元标注和多元标注:一元标注指的是只考虑当前这个词,不考虑上下文。二元标注器指的是考虑它前面的词的标注,用法只需要把上面的UnigramTagger换成BigramTagger。同理三元标注换成TrigramTagger

    组合标注器:为了提高精度和覆盖率,我们对多种标注器组合,比如组合二元标注器、一元标注器和默认标注器,如下:

    1
    2
    3
    t0 = nltk.DefaultTagger('NN')
    t1 = nltk.UnigramTagger(train_sents, backoff=t0)
    t2 = nltk.BigramTagger(train_sents, backoff=t1)

    标注器的存储:训练好的标注器为了持久化,可以存储到硬盘,具体方法如下:

    1
    2
    3
    4
    fromcPickleimportdump
    output = open('t2.pkl','wb')
    dump(t2, output,-1)
    output.close()

    使用时也可以加载,如下:

    1
    2
    3
    4
    from cPickle import load
    input = open('t2.pkl', 'rb')
    tagger = load(input)
    input.close()

    自然语言处理中的文本分类

    文本分类是机器学习在自然语言处理中的最常用也是最基础的应用,机器学习相关内容可以直接看我的有关scikit-learn相关教程,本节直接涉及nltk中的机器学习相关内容

    先来一段前戏

    机器学习的过程是训练模型和使用模型的过程,训练就是基于已知数据做统计学习,使用就是用统计学习好的模型来计算未知的数据。

    机器学习分为有监督学习和无监督学习,文本分类也分为有监督的分类和无监督的分类。有监督就是训练的样本数据有了确定的判断,基于这些已有的判断来断定新的数据,无监督就是训练的样本数据没有什么判断,完全自发的生成结论。

    无论监督学习还是无监督学习,都是通过某种算法来实现,而这种算法可以有多重选择,贝叶斯就是其中一种。在多种算法中如何选择最适合的,这才是机器学习最难的事情,也是最高境界。

    nltk中的贝叶斯分类器

    贝叶斯是概率论的鼻祖,贝叶斯定理是关于随机事件的条件概率的一则定理,贝叶斯公式是:

    P(B|A)=P(A|B)P(B)/P(A);即,已知P(A|B),P(A)和P(B)可以计算出P(B|A)。

    贝叶斯分类器就是基于贝叶斯概率理论设计的分类器算法,nltk库中已经实现,具体用法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importnltk
    my_train_set = [
    ({'feature1':u'a'},'1'),
    ({'feature1':u'a'},'2'),
    ({'feature1':u'a'},'3'),
    ({'feature1':u'a'},'3'),
    ({'feature1':u'b'},'2'),
    ({'feature1':u'b'},'2'),
    ({'feature1':u'b'},'2'),
    ({'feature1':u'b'},'2'),
    ({'feature1':u'b'},'2'),
    ({'feature1':u'b'},'2'),
    ]
    classifier = nltk.NaiveBayesClassifier.train(my_train_set)
    printclassifier.classify({'feature1':u'a'})
    printclassifier.classify({'feature1':u'b'})
    1
    2
    3
    2

    执行后判断特征a和特征b的分类分别是3和2

    因为训练集中特征是a的分类是3的最多,所以会归类为3

    当然实际中训练样本的数量要多的多,特征要多的多

    文档分类

    不管是什么分类,最重要的是要知道哪些特征是最能反映这个分类的特点,也就是特征选取。文档分类使用的特征就是最能代表这个分类的词。

    因为对文档分类要经过训练和预测两个过程,而特征的提取是这两个过程都需要的,所以,习惯上我们会把特征提取单独抽象出来作为一个公共方法,比如:

    以下代码有问题*

    1
    2
    3
    4
    5
    6
    7
    8
    9
    importnltk
    fromnltk.corpusimportmovie_reviews
    all_words = nltk.FreqDist(w.lower()forwinmovie_reviews.words())
    all_words.plot(50, cumulative=True)
    word_features = all_words.keys()[:2000]
    defdocument_features(document):
    forwordinword_features:
    features['contains(%s)'% word] = (wordindocument_words)
    returnfeatures

    这是一个简单的特征提取过程,前两行找到movie_reviews语料库中出现词频最高的2000个词作为特征,下面定义的函数就是特征提取函数,每个特征都是形如contains(***)的key,value就是True或False,表示这个词是否在文档中出现

    那么我们训练的过程就是:

    1
    2
    featuresets = [(document_features(d), c)for(d,c)indocuments]
    classifier = nltk.NaiveBayesClassifier.train(featuresets)

    要预测一个新的文档时:

    1
    classifier.classify(document_features(d))

    通过

    1
    classifier.show_most_informative_features(5)

    可以找到最优信息量的特征,这对我们选取特征是非常有帮助的

    其他文本分类

    文本分类除了文档分类外还有许多其他类型的分类,比如:

    词性标注:属于一种文本分类,一般是基于上下文语境的文本分类

    句子分割:属于标点符号的分类任务,它的特征一般选取为单独句子标识符的合并链表、数据特征(下一个词是否大写、前一个词是什么、前一个词长度……)

    识别对话行为类型:对话行为类型是指问候、问题、回答、断言、说明等

    识别文字蕴含:即一个句子是否能得出另外一个句子的结论,这可以认为是真假标签的分类任务。这是一个有挑战的事情

    教你怎么从一句话里提取出十句话的信息

    按照之前理解的内容,对一句话做处理,最多是切成一个一个的词,再标注上词性,仅此而已,然而事实并非如此,一句话还可以做更多的文章,我们本节见分晓

    什么?还能结构化?

    任何语言的每一句话之所以称为“话”,是因为它有一定的句子结构,除了一个个独立的词之外,他们之间还存在着某种关系。如果任何一句话可以由任何词构成,可长可短,那么这是一个非结构化的信息,计算机是很难理解并做计算的,但是如果能够以某种方式把句子转化成结构化的形式,计算机就可以理解了。

    实事上,人脑在理解一句话的时候也暗暗地在做着由非结构化到结构化的工作。

    比如说:“我下午要和小明在公司讨论一个技术问题”。这是一片非结构化的词语拼成的一句话,但是这里面有很多隐含信息:

    1)小明是一个实体

    2)参与者有两个:我和小明

    3)地点设定是:公司

    4)要做的事情是:讨论

    5)讨论的内容是:问题

    6)这个问题是一个技术问题

    7)公司是一个地点

    8)讨论是一种行为

    9)我和小明有某种关系

    10)下午是一个时间

    上面这些信息有一些是专门针对这个句子的,有一些是常理性的,对于针对句子的信息有利于理解这句话,对于常理性的信息可以积累下来用来以后理解其他句子。

    那么怎么才能把非结构化的句子转成结构化的信息呢?要做的工作除了断句、分词、词性标注之外,还要做的一个关键事情就是分块。

    分块

    分块就是根据句子中的词和词性,按照某种规则组合在一起形成一个个分块,每个分块代表一个实体。常见的实体包括:组织、人员、地点、日期、时间等

    以上面的例子为例,首先我们做名词短语分块(NP-chunking),比如:技术问题。名词短语分块通过词性标记和一些规则就可以识别出来,也可以通过机器学习的方法识别

    除了名词短语分块还有很多其他分块:介词短语(PP,比如:以我……)、动词短语(VP,比如:打人)、句子(S,我是人)

    分块如何标记和存储呢?

    可以采用IOB标记,I(inside,内部)、O(outside,外部)、B(begin, 开始),一个块的开始标记为B,块内的标识符序列标注为I,所有其他标识符标注为O

    也可以用树结构来存储分块,用树结构可以解决IOB无法标注的另一类分块,那就是多级分块。多级分块就是一句话可以有多重分块方法,比如:我以我的最高权利惩罚你。这里面“最高权利”、“我的最高权利”、“以我的最高权利”是不同类型分块形成一种多级分块,这是无法通过IOB标记的,但是用树结构可以。这也叫做级联分块。具体树结构举个例子:

    1
    2
    3
    4
    5
    6
    7
    (S
    (NP 小明)
    (VP
    (V 追赶)
    (NP
    (Det 一只)
    (N 兔子))))

    这是不是让你想到了语法树?

    关系抽取

    通过上面的分块可以很容易识别出实体,那么关系抽取实际就是找出实体和实体之间的关系,这是自然语言处理一个质的跨越,实体识别让机器认知了一种事物,关系识别让机器掌握了一个真相。

    关系抽取的第一个方法就是找到(X, a, Y)这种三元组,其中X和Y都是实体,a是表达关系的字符串,这完全可以通过正则来识别,因为不同语言有这不同的语法规则,所以方法都是不同的,比如中文里的“爱”可以作为这里的a,但是“和”、“因为”等就不能作为这里的a

    编程实现

    下面介绍部分有关分块的代码,因为中文标注好分块的语料没有找到,所以只能沿用英文语料来说明,但是原理是一样的

    conll2000语料中已经有标注好的分块信息,如下:

    1
    2
    fromnltk.corpusimportconll2000
    printconll2000.chunked_sents('train.txt')[99]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (S
    (PP Over/IN)
    (NP a/DT cup/NN)
    (PP of/IN)
    (NP coffee/NN)
    ,/,
    (NP Mr./NNP Stone/NNP)
    (VP told/VBD)
    (NP his/PRP$ story/NN)
    ./.)

    我们可以基于这些标注数据做训练,由于这种存储结构比较特殊,所以就不单独基于这种结构实现parser了,只说下跟前面讲的机器学习一样,只要基于这部分数据做训练,然后再用来标注新的语料就行了

    文法分析还是基于特征好啊

    语法分析固然重要,但要想覆盖语言的全部,需要进一步扩展到文法分析,文法分析可以基于规则,但是工作量难以想象,基于特征的文法分析不但可穷举,而且可以方便用计算机存储和计算,本节简单做一个介绍,更深层次的内容还需要继续关注后面的系列文章

    语法和文法

    还记得上一节中的这个吗?

    1
    2
    3
    4
    5
    6
    7
    (S
    (NP 小明)
    (VP
    (V 追赶)
    (NP
    (Det 一只)
    (N 兔子))))

    这里面的N表示名词,Det表示限定词,NP表示名词短语,V表示动词,VP表示动词短语,S表示句子

    这种句子分析方法叫做语法分析

    因为句子可以无限组合无限扩展,所以单纯用语法分析来完成自然语言处理这件事情是不可能的,所以出现了文法分析

    文法是一个潜在的无限的句子集合的一个紧凑的特性,它是通过一组形式化模型来表示的,文法可以覆盖所有结构的句子,对一个句子做文法分析,就是把句子往文法模型上靠,如果同时符合多种文法,那就是有歧义的句子

    最重要的结论:文法结构范围相当广泛,无法用规则类的方法来处理,只有利用基于特征的方法才能处理

    文法特征结构

    文法特征举例:单词最后一个字母、词性标签、文法类别、正字拼写、指示物、关系、施事角色、受事角色

    因为文法特征是一种kv,所以特征结构的存储形式是字典

    不是什么样的句子都能提取出每一个文法特征的,需要满足一定的条件,这需要通过一系列的检查手段来达到,包括:句法协议(比如this dog就是对的,而these dog就是错的)、属性和约束、术语

    特征结构的处理

    nltk帮我实现了特征结构:

    1
    2
    3
    4
    5
    importnltk
    fs1 = nltk.FeatStruct(TENSE='past', NUM='sg')
    printfs1
    fs2 = nltk.FeatStruct(POS='N', AGR=fs1)
    printfs2
    1
    2
    3
    4
    5
    6
    7
    [ NUM = 'sg' ]
    [ TENSE = 'past' ]
    [ AGR = [ NUM = 'sg' ] ]
    [ [ TENSE = 'past' ] ]
    [ ]
    [ POS = 'N' ]

    在nltk的库里已经有了一些产生式文法描述可以直接使用,位置在:

    1
    ls /usr/share/nltk_data/grammars/book_grammars
    1
    background.fol discourse.fcfg drt.fcfg feat0.fcfg feat1.fcfg german.fcfg simple-sem.fcfg sql0.fcfg sql1.fcfg storage.fcfg

    我们看其中最简单的一个sql0.fcfg,这是一个查找国家城市的sql语句的文法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    % start S
    S[SEM=(?np + WHERE + ?vp)] -> NP[SEM=?np] VP[SEM=?vp]
    VP[SEM=(?v + ?pp)] -> IV[SEM=?v] PP[SEM=?pp]
    VP[SEM=(?v + ?ap)] -> IV[SEM=?v] AP[SEM=?ap]
    NP[SEM=(?det + ?n)] -> Det[SEM=?det] N[SEM=?n]
    PP[SEM=(?p + ?np)] -> P[SEM=?p] NP[SEM=?np]
    AP[SEM=?pp] -> A[SEM=?a] PP[SEM=?pp]
    NP[SEM='Country="greece"'] -> 'Greece'
    NP[SEM='Country="china"'] -> 'China'
    Det[SEM='SELECT'] -> 'Which' | 'What'
    N[SEM='City FROM city_table'] -> 'cities'
    IV[SEM=''] -> 'are'
    A[SEM=''] -> 'located'
    P[SEM=''] -> 'in'

    解释一下

    这里面从上到下是从最大范围到最小范围一个个的解释,S是句子

    我们来加载这个文法描述,并试验如下:

    1
    2
    3
    4
    5
    6
    7
    importnltk
    fromnltkimportload_parser
    cp = load_parser('grammars/book_grammars/sql0.fcfg')
    query ='What cities are located in China'
    tokens = query.split()
    fortreeincp.parse(tokens):
    printtree
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    (S[SEM=(SELECT, City FROM city_table, WHERE, , , Country="china")]
    (NP[SEM=(SELECT, City FROM city_table)]
    (Det[SEM='SELECT'] What)
    (N[SEM='City FROM city_table'] cities))
    (VP[SEM=(, , Country="china")]
    (IV[SEM=''] are)
    (AP[SEM=(, Country="china")]
    (A[SEM=''] located)
    (PP[SEM=(, Country="china")]
    (P[SEM=''] in)
    (NP[SEM='Country="china"'] China)))))

    我们可以看到用特征结构可以建立对大量广泛的语言学现象的简介分析

    重温自然语言处理

    自然语言处理怎么学?

    先学会倒着学,倒回去看上面那句话:不管三七二十一先用起来,然后再系统地学习

    nltk是最经典的自然语言处理的python库,不知道怎么用的看前几篇文章吧,先把它用起来,最起码做出来一个词性标注的小工具

    自然语言处理学什么?

    这门学科的知识可是相当的广泛,广泛到你不需要掌握任何知识就可以直接学,因为你不可能掌握它依赖的全部知识,所以就直接冲过去吧。。。

    话说回来,它到底包括哪些知识呢?如果把这些知识比作难关的话,我数一数,整整九九八十一难

    第一难:语言学。直接懵逼了吧?语言学啥玩意,怎么说话?还是怎么学说话?其实大家也都说不清语言学是什么东东,但是我知道大家在这方面都在研究啥,有的在研究语言描述的学问,有的在研究语言理论的学问,有的在研究不同语言对比的学问,有的在研究语言共同点上的学问,有的在研究语言发展的历史,有的在研究语言的结构,总之用一个字来形容那是一个涉猎广泛啊

    第二难:语音学。再一次懵逼!有人说:我知道!语音学就是怎么发声。赞一个,回答的那是相当不完全对啊!你以为你是学唱歌吗?语音学研究领域分三块:一块是研究声音是怎么发出来的(同学你说对了一点);一块是研究声音是怎么传递的;一块是研究声音是怎么接收的。这尼玛不是物理吗?怎么还整出语言学来了?其实这是一个交叉学科,交叉了语言学,交叉了生物学

    第三难:概率论。啥?怎么到处都是概率论啊?听人说今年又某某某得了诺贝尔经济学奖了,我定睛一看,尼玛,这不是研究的概率论嘛,这也能得经济学奖,真是得数学者得天下啊。废话少说,概率论跟自然语言处理有什么关系?我知道了,说话是一个概率问题,我经常说“尼玛”,那我再次说“尼玛”的概率就高,嗯~沾边了。提到概率论那就少不了这些:贝叶斯老爷爷、马尔可夫大叔……

    第四难:信息论。提到信息论肯定第一个想到的是“香浓”啊,有点流口水了,香农老爷爷提出的熵理论影响那是相当巨大啊,没有它估计就没有我们计算机人事什么事了,因为没有他就没有互联网了。还有人说没有图灵就没有计算机了,他咋不说没有他们俩就没有地球了呢?

    第五难:机器学习。机器学习是我的最爱,得聊的正式一点,咳咳!机器学习啊——得好好学

    第六难:形式语言与自动机。我滴妈啊!我跪了!刚说图灵图灵就来了。说白了,形式语言就是把语言搞的很形式,换句话说就是本来你能懂的东西,搞成你不能懂的东西,那就是形式语言啦!不信?你听:短语结构语言、上下文有关语言、上下文无关语言、正则语言,懂了吗?而自动机呢包括:图灵机、有穷自动机、下推自动机、线性有界自动机。你可能会问了,这么多自动机那得要多少汽油啊?该死的翻译怎么就把这么高大上的英文给翻译成这么晦涩呢,自动机英文叫automata,表达的是自动形成一些信息,也就是说根据前面能自动判断出后面。形式语言用在自然语言处理上我理解,都有语言俩字,可是这自动机有什么用呢?这您还真问着了,您见过拼写检查吗?这就是用的自动机,用处杠杠的!

    第七难:语言知识库。你见过科幻电影里的机器人手捧着电话线就能知道一切的镜头吧,互联网上有无数文本内容,用我们抽象的话说那都是知识,但是简单放在电脑里那就是一串字符串,怎么才能让它以知识的形式存储呢?首先得让计算机能分析语言,那么语料就是它学习的基础、是种子,然后有了基础再让它把语言里的知识存储起来,这就形成了语言知识库

    第八难:语言模型。模型顾名思义就是“模子”,就是“往上靠”的意思,怎么靠上去更吻合就怎么靠,这就是语言模型。怎么?没懂?好那我用形式化的语言再来说一下:把很多已经整理好的模子放在那里,遇到一个新内容的时候看看属于哪种格式,那就按照这种模子来解释。嗯~你肯定懂了

    第九难:分词、实体识别、词性标注。这部分开始纯语言处理了,前几节也简单讲过这部分内容,分词就是让计算机自动区分出汉字组成的词语,因为一个词语一个意思嘛。实体识别就是再分词之后能够根据各种短语形式判断出哪个词表示的是一个物体或组织或人名或……。词性标注就是给你一句话,你能识别出“名动形、数量代、副介连助叹拟声”。

    第十难:句法分析。句法分析类似于小学时学的主谓宾补定状的区分,只是要在这基础上组合成短语,也就是把一个非结构化的句子分析称结构化的数据结构

    第十一难:语义分析。看起来一步一步越来越深入了。语义是基于句法分析,进一步理解句子的意思,最重要的就是消除歧义,人姑且还会理解出歧义来呢,何况一个机器

    第十二难:篇章分析。一堆堆的句子,每个都分析明白了,但是一堆句子组合成的篇章又怎么才能联系起来呢?你得总结出本文的中心思想不是?这他娘的是小学语文里最难的一道题

    以上这些内容就是自然语言处理的几大难了,什么?说好的九九八十一难呢?你还不嫌多啊?你还真想变成孙猴子吗?能把这几关过了就不错了!

    自然语言处理和聊天机器人什么关系?

    说到这里,索性就说说下自然语言处理的应用领域吧。

    第一个应用:机器翻译。机器翻译方法有很多,我不做,也不说,想学自己看去

    第二个应用:语音翻译。跟上一个不是一个吗?不是的,这是语音,那个是机器

    第三个应用:文本分类与情感分析。别看两个词,其实是一种事——分类。前面有很多篇文章将文本分类的,可以看看,还有代码噢。

    第四个应用:信息检索与问答系统。终于说到重点了,累死我了。这里的问答系统就是我们的聊天机器人。后面会着重讲这个应用,我不断读论文,不断给大家分享哈,别着急,乖!

    第五个应用:自动文摘和信息抽取。看过百度搜索里显示的摘要吗?他们多么的精简,而且描述了网页里的中心内容,多漂亮啊!可惜多数都没做到自动文摘。所以这是一个高技术难度的问题。

    第六个应用:人机对话。融合了语音识别、口语情感分析、再加上问答系统的全部内容,是自然语言处理的最高境界,离机器人统霸世界不远了。

    以及这么多数不完的应用:文本挖掘、舆情分析、隐喻计算、文字编辑和自动校对、作文自动评分、OCR、说话人识别验证……

    好!自然语言处理就温习到这里,让我们上阵出发!

    聊天机器人应该怎么做

    聊天机器人到底该怎么做呢?我日思夜想,于是乎我做了一个梦,梦里面我完成了我的聊天机器人,它叫chatbot,经过我的一番盘问,它向我叙述了它的诞生记

    聊天机器人是可行的

    我:chatbot,你好!

    chatbot:你也好!

    我:聊天机器人可行吗?

    chatbot:你不要怀疑这是天方夜谭,我不就在这里吗?世界上还有很多跟我一样聪明的机器人呢,你听过IBM公司在2010年就研发出来了的Watson问答系统吗?它可比我要聪明100倍呢

    我:噢,想起来了,据说Watson在智力竞赛中竟然战胜了人类选手。但是我了解到它有一些缺陷:因为它还只是对信息检索技术的综合运用,并没有进行各种语义关系的深刻计算,所以它能回答的问题也仅限于实事类的问题,所以它能赢得也就是知识类的智力竞赛,如果你给它出个脑筋急转弯,它就不行了

    chatbot:是的呢,所以你任重道远啊

    聊天机器人工作原理是什么

    我:chatbot,我问的每一句话,你都是怎么处理并回答我的呢?

    chatbot:我身体里有三个重要模块:提问处理模块、检索模块、答案抽取模块。三个模块一起工作,就能回答你的问题啦

    我:是嘛,那么这个提问处理模块是怎么工作的呢?

    chatbot:提问处理模块要做三项重要工作:查询关键词生成、答案类型确定、句法和语义分析。

    我:那么这个查询关。。。

    chatbot:别急别急,听我一个一个讲给你听。查询关键词生成,就是从你的提问中提取出关键的几个关键词,因为我本身是一个空壳子,需要去网上查找资料才能回答你,而但网上资料那么多,我该查哪些呢?所以你的提问就有用啦,我找几个中心词,再关联出几个扩展词,上网一搜,一大批资料就来啦,当然这些都是原始资料,我后面要继续处理。再说答案类型确定,这项工作是为了确定你的提问属于哪一类的,如果你问的是时间、地点,和你问的是技术方案,那我后面要做的处理是不一样的。最后再说这个句法和语义分析,这是对你问题的深层含义做一个剖析,比如你的问题是:聊天机器人怎么做?那么我要知道你要问的是聊天机器人的研发方法

    我:原来是这样,提问处理模块这三项工作我了解了,那么检索模块是怎么工作的呢?

    chatbot:检索模块跟搜索引擎比较像,就是根据查询关键词所信息检索,返回句子或段落,这部分就是下一步要处理的原料

    我:那么答案抽取模块呢?

    chatbot:答案抽取模块可以说是计算量最大的部分了,它要通过分析和推理从检索出的句子或段落里抽取出和提问一致的实体,再根据概率最大对候选答案排序,注意这里是“候选答案”噢,也就是很难给出一个完全正确的结果,很有可能给出多个结果,最后还在再选出一个来

    我:那么我只要实现这三个模块,就能做成一个你喽?

    chatbot:是的

    聊天机器人的关键技术

    我:chatbot,小弟我知识匮乏,能不能告诉我都需要学哪些关键技术才能完成我的梦想

    chatbot:小弟。。。我还没满月。说到关键技术,那我可要列一列了,你的任务艰巨了:

    1)海量文本知识表示:网络文本资源获取、机器学习方法、大规模语义计算和推理、知识表示体系、知识库构建;

    2)问句解析:中文分词、词性标注、实体标注、概念类别标注、句法分析、语义分析、逻辑结构标注、指代消解、关联关系标注、问句分类(简单问句还是复杂问句、实体型还是段落型还是篇章级问题)、答案类别确定;

    3)答案生成与过滤:候选答案抽取、关系推演(并列关系还是递进关系还是因果关系)、吻合程度判断、噪声过滤

    聊天机器人的技术方法

    我:chatbot,我对聊天机器人的相关技术总算有所了解了,但是我具体要用什么方法呢?

    chatbot:看你这么好学,那我就多给你讲一讲。聊天机器人的技术可以分成四种类型:1)基于检索的技术;2)基于模式匹配的技术;3)基于自然语言理解的技术;4)基于统计翻译模型的技术。这几种技术并不是都要实现,而是选其一,听我给你说说他们的优缺点,你就知道该选哪一种了。基于检索的技术就是信息检索技术,它简单易实现,但无法从句法关系和语义关系给出答案,也就是搞不定推理问题,所以直接pass掉。基于模式匹配的技术就是把问题往已经梳理好的几种模式上去靠,这种做推理简单,但是模式我们涵盖不全,所以也pass掉。基于自然语言理解就是把浅层分析加上句法分析、语义分析都融入进来做的补充和改进。基于统计翻译就是把问句里的疑问词留出来,然后和候选答案资料做配对,能对齐了就是答案,对不齐就对不起了,所以pass掉。选哪个知道了吗?

    我:知道了!基于自然语言理解的技术!so easy!妈妈再也不用担心我的学习!o(╯□╰)o

    半个小时搞定词性标注与关键词提取

    想要做到和人聊天,首先得先读懂对方在说什么,所以问句解析是整个聊天过程的第一步,问句解析是一个涉及知识非常全面的过程,几乎涵盖了自然语言处理的全部,本节让我们尝试一下如何分析一个问句

    问句解析的过程

    一般问句解析需要进行分词、词性标注、命名实体识别、关键词提取、句法分析以及查询问句分类等。这些事情我们从头开始做无非是重复造轮子,傻子才会这么做,人之所以为人是因为会使用工具。网络上有关中文的NLP工具有很多,介绍几个不错的:

    第一个要数哈工大的LTP(语言技术平台)了,它可以做中文分词、词性标注、命名实体识别、依存句法分析、语义角色标注等丰富、 高效、精准的自然语言处理技术

    第二个就是博森科技了,它除了做中文分词、词性标注、命名实体识别、依存文法之外还可以做情感分析、关键词提取、新闻分类、语义联想、时间转换、新闻摘要等,但因为是商业化的公司,除了分词和词性标注免费之外全都收费

    第三个就是jieba分词,这个开源小工具分词和词性标注做的挺不错的,但是其他方面还欠缺一下,如果只是中文分词的需求完全可以满足

    第四个就是中科院张华平博士的NLPIR汉语分词系统,也能支持关键词提取

    我们优先选择NLPIR

    NLPIR使用

    文档在http://pynlpir.readthedocs.io/en/latest/

    首先安装pynlpir库

    1
    2
    pip install pynlpir
    pynlpir update

    若出现授权错误,则去https://github.com/NLPIR-team/NLPIR/tree/master/License/license%20for%20a%20month/NLPIR-ICTCLAS%E5%88%86%E8%AF%8D%E7%B3%BB%E7%BB%9F%E6%8E%88%E6%9D%83下载授权文件NLPIR.user, 覆盖到/usr/local/lib/python2.7/dist-packages/pynlpir/Data/

    写个小程序测试一下分词效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importpynlpir
    pynlpir.open()
    s ='聊天机器人到底该怎么做呢?'
    segments = pynlpir.segment(s)
    forsegmentinsegments:
    printsegment[0],'\t', segment[1]
    pynlpir.close()
    1
    2
    3
    4
    5
    6
    7
    8
    聊天 verb
    机器人 noun
    到底 adverb
    该 verb
    怎么 pronoun
    做 verb
    呢 modal particle
    ? punctuation mark

    下面我们再继续试下关键词提取效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importpynlpir
    pynlpir.open()
    s ='聊天机器人到底该怎么做呢?'
    key_words = pynlpir.get_key_words(s, weighted=True)
    forkey_wordinkey_words:
    printkey_word[0],'\t', key_word[1]
    pynlpir.close()
    1
    2
    聊天 2.0
    机器人 2.0

    从这个小程序来看,分词和关键词提取效果很好

    下面我们再来试验一个,这一次我们把分析功能全打开,部分代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importpynlpir
    pynlpir.open()
    s ='海洋是如何形成的'
    segments = pynlpir.segment(s, pos_names='all', pos_english=False)
    forsegmentinsegments:
    printsegment[0],'\t', segment[1]
    key_words = pynlpir.get_key_words(s, weighted=True)
    forkey_wordinkey_words:
    printkey_word[0],'\t', key_word[1]
    pynlpir.close()
    1
    2
    3
    4
    5
    6
    7
    8
    海洋 noun
    是 verb:verb 是
    如何 pronoun:interrogative pronoun:predicate interrogative pronoun
    形成 verb
    的 particle:particle 的/底
    海洋 2.0
    形成 2.0

    如果我们把segments在加上一个参数pos_english=False,也就是不使用英语,那么输出就是

    1
    2
    3
    4
    5
    6
    7
    8
    海洋 名词
    是 动词:动词"是"
    如何 代词:疑问代词:谓词性疑问代词
    形成 动词
    的 助词:的/底
    海洋 2.0
    形成 2.0

    解释一下

    这里的segment是切词的意思,返回的是tuple(token, pos),其中token就是切出来的词,pos就是语言属性

    调用segment方法指定的pos_names参数可以是’all’, ‘child’, ‘parent’,默认是parent, 表示获取该词性的最顶级词性,child表示获取该词性的最具体的信息,all表示获取该词性相关的所有词性信息,相当于从其顶级词性到该词性的一条路径

    词性分类表

    查看nlpir的源代码中的pynlpir/docs/pos_map.rst,可以看出全部词性分类及其子类别如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    POS_MAP = {
    'n': ('名词', 'noun', {
    'nr': ('人名', 'personal name', {
    'nr1': ('汉语姓氏', 'Chinese surname'),
    'nr2': ('汉语名字', 'Chinese given name'),
    'nrj': ('日语人名', 'Japanese personal name'),
    'nrf': ('音译人名', 'transcribed personal name')
    }),
    'ns': ('地名', 'toponym', {
    'nsf': ('音译地名', 'transcribed toponym'),
    }),
    'nt': ('机构团体名', 'organization/group name'),
    'nz': ('其它专名', 'other proper noun'),
    'nl': ('名词性惯用语', 'noun phrase'),
    'ng': ('名词性语素', 'noun morpheme'),
    }),
    't': ('时间词', 'time word', {
    'tg': ('时间词性语素', 'time morpheme'),
    }),
    's': ('处所词', 'locative word'),
    'f': ('方位词', 'noun of locality'),
    'v': ('动词', 'verb', {
    'vd': ('副动词', 'auxiliary verb'),
    'vn': ('名动词', 'noun-verb'),
    'vshi': ('动词"是"', 'verb 是'),
    'vyou': ('动词"有"', 'verb 有'),
    'vf': ('趋向动词', 'directional verb'),
    'vx': ('行事动词', 'performative verb'),
    'vi': ('不及物动词', 'intransitive verb'),
    'vl': ('动词性惯用语', 'verb phrase'),
    'vg': ('动词性语素', 'verb morpheme'),
    }),
    'a': ('形容词', 'adjective', {
    'ad': ('副形词', 'auxiliary adjective'),
    'an': ('名形词', 'noun-adjective'),
    'ag': ('形容词性语素', 'adjective morpheme'),
    'al': ('形容词性惯用语', 'adjective phrase'),
    }),
    'b': ('区别词', 'distinguishing word', {
    'bl': ('区别词性惯用语', 'distinguishing phrase'),
    }),
    'z': ('状态词', 'status word'),
    'r': ('代词', 'pronoun', {
    'rr': ('人称代词', 'personal pronoun'),
    'rz': ('指示代词', 'demonstrative pronoun', {
    'rzt': ('时间指示代词', 'temporal demonstrative pronoun'),
    'rzs': ('处所指示代词', 'locative demonstrative pronoun'),
    'rzv': ('谓词性指示代词', 'predicate demonstrative pronoun'),
    }),
    'ry': ('疑问代词', 'interrogative pronoun', {
    'ryt': ('时间疑问代词', 'temporal interrogative pronoun'),
    'rys': ('处所疑问代词', 'locative interrogative pronoun'),
    'ryv': ('谓词性疑问代词', 'predicate interrogative pronoun'),
    }),
    'rg': ('代词性语素', 'pronoun morpheme'),
    }),
    'm': ('数词', 'numeral', {
    'mq': ('数量词', 'numeral-plus-classifier compound'),
    }),
    'q': ('量词', 'classifier', {
    'qv': ('动量词', 'verbal classifier'),
    'qt': ('时量词', 'temporal classifier'),
    }),
    'd': ('副词', 'adverb'),
    'p': ('介词', 'preposition', {
    'pba': ('介词“把”', 'preposition 把'),
    'pbei': ('介词“被”', 'preposition 被'),
    }),
    'c': ('连词', 'conjunction', {
    'cc': ('并列连词', 'coordinating conjunction'),
    }),
    'u': ('助词', 'particle', {
    'uzhe': ('着', 'particle 着'),
    'ule': ('了/喽', 'particle 了/喽'),
    'uguo': ('过', 'particle 过'),
    'ude1': ('的/底', 'particle 的/底'),
    'ude2': ('地', 'particle 地'),
    'ude3': ('得', 'particle 得'),
    'usuo': ('所', 'particle 所'),
    'udeng': ('等/等等/云云', 'particle 等/等等/云云'),
    'uyy': ('一样/一般/似的/般', 'particle 一样/一般/似的/般'),
    'udh': ('的话', 'particle 的话'),
    'uls': ('来讲/来说/而言/说来', 'particle 来讲/来说/而言/说来'),
    'uzhi': ('之', 'particle 之'),
    'ulian': ('连', 'particle 连'),
    }),
    'e': ('叹词', 'interjection'),
    'y': ('语气词', 'modal particle'),
    'o': ('拟声词', 'onomatopoeia'),
    'h': ('前缀', 'prefix'),
    'k': ('后缀' 'suffix'),
    'x': ('字符串', 'string', {
    'xe': ('Email字符串', 'email address'),
    'xs': ('微博会话分隔符', 'hashtag'),
    'xm': ('表情符合', 'emoticon'),
    'xu': ('网址URL', 'URL'),
    'xx': ('非语素字', 'non-morpheme character'),
    }),
    'w': ('标点符号', 'punctuation mark', {
    'wkz': ('左括号', 'left parenthesis/bracket'),
    'wky': ('右括号', 'right parenthesis/bracket'),
    'wyz': ('左引号', 'left quotation mark'),
    'wyy': ('右引号', 'right quotation mark'),
    'wj': ('句号', 'period'),
    'ww': ('问号', 'question mark'),
    'wt': ('叹号', 'exclamation mark'),
    'wd': ('逗号', 'comma'),
    'wf': ('分号', 'semicolon'),
    'wn': ('顿号', 'enumeration comma'),
    'wm': ('冒号', 'colon'),
    'ws': ('省略号', 'ellipsis'),
    'wp': ('破折号', 'dash'),
    'wb': ('百分号千分号', 'percent/per mille sign'),
    'wh': ('单位符号', 'unit of measure sign'),
    }),
    }

    好,这回我们一下子完成了分词、词性标注、关键词提取。命名实体识别、句法分析以及查询问句分类我们之后再研究

    输出文本,输出提切结果,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # coding:utf-8
    import sys
    reload(sys)
    sys.setdefaultencoding( "utf-8" )
    import pynlpir
    pynlpir.open()
    s = raw_input('请输入语句:')
    segments = pynlpir.segment(s, pos_names='all', pos_english=False)
    for segment in segments:
    print segment[0], '\t', segment[1]
    key_words = pynlpir.get_key_words(s, weighted=True)
    for key_word in key_words:
    print key_word[0], '\t', key_word[1]
    pynlpir.close()

    0字节存储海量语料资源

    基于语料做机器学习需要海量数据支撑,如何能不存一点数据获取海量数据呢?我们可以以互联网为强大的数据后盾,搜索引擎为我们提供了高效的数据获取来源,结构化的搜索结果展示为我们实现了天然的特征基础,唯一需要我们做的就是在海量结果中选出我们需要的数据,本节我们来探索如何利用互联网拿到我们所需的语料资源

    关键词提取

    互联网资源无穷无尽,如何获取到我们所需的那部分语料库呢?这需要我们给出特定的关键词,而基于问句的关键词提取上一节已经做了介绍,利用pynlpir库可以非常方便地实现关键词提取,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importpynlpir
    pynlpir.open()
    s ='怎么才能把电脑里的垃圾文件删除'
    key_words = pynlpir.get_key_words(s, weighted=True)
    forkey_wordinkey_words:
    printkey_word[0],'\t', key_word[1]
    pynlpir.close()

    提取出的关键词如下:

    1
    2
    3
    4
    电脑 2.0
    垃圾 2.0
    文件 2.0
    删除 1.0

    我们基于这四个关键词来获取互联网的资源就可以得到我们所需要的语料信息

    充分利用搜索引擎

    有了关键词,想获取预料信息,还需要知道几大搜索引擎的调用接口,首先我们来探索一下百度,百度的接口是这样的:

    https://www.baidu.com/s?wd=机器学习 数据挖掘 信息检索

    把wd参数换成我们的关键词就可以拿到相应的结果,我们用程序来尝试一下:

    首先创建scrapy工程,执行:

    1
    scrapy startproject baidu_search

    自动生成了baidu_search目录和下面的文件(不知道怎么使用scrapy,请见我的文章教你成为全栈工程师(Full Stack Developer) 三十-十分钟掌握最强大的python爬虫)

    创建baidu_search/baidu_search/spiders/baidu_search.py文件,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    importscrapy
    classBaiduSearchSpider(scrapy.Spider):
    name ="baidu_search"
    allowed_domains = ["baidu.com"]
    start_urls = [
    "https://www.baidu.com/s?wd=机器学习"
    ]
    defparse(self, response):
    printresponse.body

    这样我们的抓取器就做好了,进入baidu_search/baidu_search/目录,执行:

    1
    scrapy crawl baidu_search

    我们发现返回的数据是空,下面我们修改配置来解决这个问题,修改settings.py文件,把ROBOTSTXT_OBEY改为

    1
    ROBOTSTXT_OBEY = False

    并把USER_AGENT设置为:

    1
    USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'

    为了避免抓取hang住,我们添加如下超时设置:

    1
    DOWNLOAD_TIMEOUT = 5

    再次执行

    1
    scrapy crawl baidu_search

    这次终于可以看到大片大片的html了,我们临时把他写到文件中,修改parse()函数如下:

    1
    2
    3
    4
    def parse(self, response):
    filename = "result.html"
    with open(filename, 'wb') as f:
    f.write(response.body)

    重新执行后生成了result.html,我们用浏览器打开本地文件。

    语料提取

    上面得到的仅是搜索结果,它只是一种索引,真正的内容需要进入到每一个链接才能拿到,下面我们尝试提取出每一个链接并继续抓取里面的内容,那么如何提取链接呢,我们来分析一下result.html这个抓取百度搜索结果文件

    我们可以看到,每一条链接都是嵌在class=c-container这个div里面的一个h3下的a标签的href属性

    所以我们的提取规则就是:

    1
    hrefs = response.selector.xpath('//div[contains(@class, "c-container")]/h3/a/@href').extract()

    修改parse()函数并添加如下代码:

    1
    2
    3
    hrefs = response.selector.xpath('//div[contains(@class, "c-container")]/h3/a/@href').extract()
    forhrefinhrefs:
    printhref

    执行打印出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ......
    2017-08-21 22:26:17 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.baidu.com/s?wd=%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0> (referer: None)
    http://www.baidu.com/link?url=evTTw58FTb9_-gGNUuOgv3_coiSBhpi-4ZQKoLQZvPXbsj3kfzROuH4cm1CxPqSqWl_E1vamGMyOHAO1G3jXEMyXoF0fHSbGmWzX99tDkCnTLIrR8-oysqnEb7VzI2EC
    http://www.baidu.com/link?url=T0QwDAL_1ypuWLshTeFSh9Tkl3wQVwQFaY_sjIRY1G7TaBq2YC1SO2T2mLsLTiC2tFJ878OihtiIkEgepJNG-q
    http://www.baidu.com/link?url=xh1H8nE-T4CeAq_A3hGbNwHfDKs6K7HCprH6DrT29yv6vc3t_DSk6zq7_yekiL_iyd9rGxMSONN_wJDwpjqNAK
    http://www.baidu.com/link?url=LErVCtq1lVKbh9QAAjdg37GW1_B3y8g_hjoafChZ90ycuG3razgc9X_lE4EgiibkjzCPImQOxTl-b5LOwZshtxSf7sCTOlBpLRjcMyG2Fc7
    http://www.baidu.com/link?url=vv_KA9CNJidcGTV1SE096O9gXqVC7yooCDMVvCXg9Vg22nZW2eBIq9twWSFh17VVYqNJ26wkRJ7XKuTsD3-qFDdi5_v-AZZrDeNI07aZaYG
    http://www.baidu.com/link?url=dvMowOWWPV3kEZxzy1q7W2OOBuph0kI7FuZTwp5-ejsU-f-Aiif-Xh7U4zx-qoKW_O1fWVwutJuOtEWr2A7cwq
    http://www.baidu.com/link?url=evTTw58FTb9_-gGNUuOgvHC_aU-RmA0z8vc7dTH6-tgzMKuehldik7N_vi0s4njGvLo13id-kuGnVhhmfLV3051DpQ7CLO22rKxCuCicyTe
    http://www.baidu.com/link?url=QDkm6sUGxi-qYC6XUYR2SWB_joBm_-25-EXUSyLm9852gQRu41y-u_ZPG7SKhjs6U_l99ZcChBNXz4Ub5a0RJa
    http://www.baidu.com/link?url=Y9Qfk4m6Hm8gQ-7XgUzAl5b-bgxNOBn_e_7v9g6XHmsZ_4TK8Mm1E7ddQaDUAdSCxjgJ_Ao-vYA6VZjfVuIaX58PlWo_rV8AqhVrDA1Bd0W
    http://www.baidu.com/link?url=X7eU-SPPEylAHBTIGhaET0DEaLEaTYEjknjI2_juK7XZ2D2eb90b1735tVu4vORourw_E68sZF8P2O4ghTVcQa
    2017-08-21 22:26:17 [scrapy.core.engine] INFO: Closing spider (finished)
    ...

    下面我们把这些url添加到抓取队列中继续抓取,修改baidu_search.py文件,如下:

    1
    2
    3
    4
    5
    6
    7
    def parse(self, response):
    hrefs = response.selector.xpath('//div[contains(@class, "c-container")]/h3/a/@href').extract()
    for href in hrefs:
    yield scrapy.Request(href, callback=self.parse_url)
    def parse_url(self, response):
    print len(response.body)

    抓取效果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    2017-08-21 22:29:50 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.baidu.com/s?wd=%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0> (referer: None)
    2017-08-21 22:29:50 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://www.leiphone.com/news/201609/SJGulTsdGcisR8Wz.html> from <GET http://www.baidu.com/link?url=iQ6rC78zm48BmQQ2GcK8ffphKCITxraUEcyS1waz7_yn5JLl5ZJgKerMXO1yQozfC9vxN0C89iU0Rd2nwEFXoEj1doqsbCupuDARBEtlHW3>
    2017-08-21 22:29:50 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://blog.jobbole.com/tag/machinelearning/> from <GET http://www.baidu.com/link?url=XIi6gyYcL8XtxD7ktWfZssm0Z2nO-TY5xzrHI8TLOMnUWj8a9u4swB3KI66yhT4wrqXhjxyRq95s5PkHlsplwq>
    2017-08-21 22:29:50 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET https://www.zhihu.com/question/33892253> from <GET http://www.baidu.com/link?url=tP0PBScNvaht7GL1qoJCQQzfNpdmDK_Cw5FNF3xVwluaYwlLjWxEzgtFalHtai1KNd7XD4h54LlrmI2ZGgottK>
    2017-08-21 22:29:50 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://open.163.com/special/opencourse/machinelearning.html> from <GET http://www.baidu.com/link?url=vZdsoRD6urZDhxJGRHNJJ7vSeTfI8mdkH0F01gkG24x9hj5HjiWPU7bsdDtJJMvEi-x4QIjX-hG5pQ4AWpeIq2u7NddTwiDDrXwRZF9_Sxe>
    2017-08-21 22:29:50 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://tieba.baidu.com/f?kw=%BB%FA%C6%F7%D1%A7%CF%B0&fr=ala0&tpl=5> from <GET http://www.baidu.com/link?url=nSVlWumopaJ_gz-bWMvVTQBtLY8E0LkwP3gPc86n26XQ9WDdlsI_1pNAVGa_4YSYoKpHiUy2qcBdJOvQuxcvEmBFPGufpbHsCA3ia2t_-HS>
    2017-08-21 22:29:50 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://tech.163.com/16/0907/07/C0BHQND400097U80.html> from <GET http://www.baidu.com/link?url=g7VePX8O7uHmJphvogYc6U8uMKbIbVSuFQAUw05fmD-tPTr4T9yvS4sbDZCZ8FYBelGq95nCpAJhghsiQf_hoq>
    2017-08-21 22:29:50 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET https://www.zhihu.com/question/20691338> from <GET http://www.baidu.com/link?url=pjwLpE4UAddN9It0yK3-Ypr6MDcAciWoNMBb5GOnX0-Xi-vV3A1ZbWv32oCRwMoIKwa__pPdOxVTzrCu7d9zz_>

    看起来能够正常抓取啦,下面我们把抓取下来的网页提取出正文并尽量去掉标签,如下:

    1
    2
    defparse_url(self, response):
    printremove_tags(response.selector.xpath('//body').extract()[0])

    下面,我们希望把百度搜索结果中的摘要也能够保存下来作为我们语料的一部分,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    defparse(self, response):
    hrefs = response.selector.xpath('//div[contains(@class, "c-container")]/h3/a/@href').extract()
    containers = response.selector.xpath('//div[contains(@class, "c-container")]')
    forcontainerincontainers:
    href = container.xpath('h3/a/@href').extract()[0]
    title = remove_tags(container.xpath('h3/a').extract()[0])
    c_abstract = container.xpath('div/div/div[contains(@class, "c-abstract")]').extract()
    abstract =""
    iflen(c_abstract) >0:
    abstract = remove_tags(c_abstract[0])
    request = scrapy.Request(href, callback=self.parse_url)
    request.meta['title'] = title
    request.meta['abstract'] = abstract
    yieldrequest
    defparse_url(self, response):
    print"url:", response.url
    print"title:", response.meta['title']
    print"abstract:", response.meta['abstract']
    content = remove_tags(response.selector.xpath('//body').extract()[0])
    print"content_len:", len(content)

    解释一下,首先我们在提取url的时候顺便把标题和摘要都提取出来,然后通过scrapy.Request的meta传递到处理函数parse_url中,这样在抓取完成之后也能接到这两个值,然后提取出content,这样我们想要的数据就完整了:url、title、abstract、content

    百度搜索数据几乎是整个互联网的镜像,所以你想要得到的答案,我们的语料库就是整个互联网,而我们完全借助于百度搜索引擎,不必提前存储任何资料,互联网真是伟大!

    之后这些数据想保存在什么地方就看后面我们要怎么处理了,欲知后事如何,且听下回分解

    教你如何利用强大的中文语言技术平台做依存句法和语义依存分析

    句法分析是自然语言处理中非常重要的环节,没有句法分析是无法让计算机理解语言的含义的,依存句法分析由法国语言学家在1959年提出,影响深远,并且深受计算机行业青睐,依存句法分析也是做聊天机器人需要解决的最关键问题之一,语义依存更是对句子更深层次的分析,当然,有可用的工具我们就不重复造轮子,本节介绍如何利用国内领先的中文语言技术平台实现句法分析

    什么是依存句法分析呢?

    叫的晦涩的术语,往往其实灰常简单,句法就是句子的法律规则,也就是句子里成分都是按照什么法律规则组织在一起的。而依存句法就是这些成分之间有一种依赖关系。什么是依赖:没有你的话,我存在就是个错误。“北京是中国的首都”,如果没有“首都”,那么“中国的”存在就是个错误,因为“北京是中国的”表达的完全是另外一个意思了。

    什么是语义依存分析呢?

    “语义”就是说句子的含义,“张三昨天告诉李四一个秘密”,那么语义包括:谁告诉李四秘密的?张三。张三告诉谁一个秘密?李四。张三什么时候告诉的?昨天。张三告诉李四什么?秘密。

    语义依存和依存句法的区别

    依存句法强调介词、助词等的划分作用,语义依存注重实词之间的逻辑关系

    另外,依存句法随着字面词语变化而不同,语义依存不同字面词语可以表达同一个意思,句法结构不同的句子语义关系可能相同。

    依存句法分析和语义依存分析对我们的聊天机器人有什么意义呢?

    依存句法分析和语义分析相结合使用,对对方说的话进行依存和语义分析后,一方面可以让计算机理解句子的含义,从而匹配到最合适的回答,另外如果有已经存在的依存、语义分析结果,还可以通过置信度匹配来实现聊天回答。

    依存句法分析到底是怎么分析的呢?

    依存句法分析的基本任务是确定句式的句法结构(短语结构)或句子中词汇之间的依存关系。依存句法分析最重要的两棵树:

    依存树:子节点依存于父节点

    依存投射树:实线表示依存联结关系,位置低的成分依存于位置高的成分,虚线为投射线

    image

    依存关系的五条公理

    1. 一个句子中只有一个成分是独立的

    2. 其他成分直接依存于某一成分

    3. 任何一个成分都不能依存于两个或两个以上的成分

    4. 如果A成分直接依存于B成分,而C成分在句子中位于A和B之间,那么C或者直接依存于B,或者直接依存于A和B之间的某一成分

    5. 中心成分左右两面的其他成分相互不发生关系

    什么地方存在依存关系呢?比如合成词(如:国内)、短语(如:英雄联盟)很多地方都是

    LTP依存关系标记

    关系简称全程示例
    主谓关系SBVsubject-verb我送她一束花 (我 <– 送)
    动宾关系VOB 直接宾语verb-object我送她一束花 (送 –> 花)
    间宾关系IOB 间接宾语indirect-object我送她一束花 (送 –> 她)
    前置宾语FOB 前置宾语fronting-object他什么书都读 (书 <– 读)
    兼语DBLdouble他请我吃饭 (请 –> 我)
    定中关系ATTattribute红苹果 (红 <– 苹果)
    状中结构ADVadverbial非常美丽 (非常 <– 美丽)
    动补结构CMPcomplement做完了作业 (做 –> 完)
    并列关系COOcoordinate大山和大海 (大山 –> 大海)
    介宾关系POBpreposition-object在贸易区内 (在 –> 内)
    左附加关系LADleft adjunct大山和大海 (和 <– 大海)
    右附加关系RADright adjunct孩子们 (孩子 –> 们)
    独立结构ISindependent structure两个单句在结构上彼此独立
    核心关系HEDhead指整个句子的核心

    那么依存关系是怎么计算出来的呢?

    是通过机器学习和人工标注来完成的,机器学习依赖人工标注,那么都哪些需要我们做人工标注呢?分词词性、依存树库、语义角色都需要做人工标注,有了这写人工标注之后,就可以做机器学习来分析新的句子的依存句法了

    LTP云平台怎么用?

    http://www.ltp-cloud.com/

    把语言模型探究到底

    无论什么做自然语言处理的工具,都是基于计算机程序实现的,而计算机承担了数学计算的职责,那么自然语言和数学之间的联系就是语言模型,只有理解语言模型才能理解各种工具的实现原理,本节让我们深究语言模型的世界

    什么是数学模型

    数学模型是运用数理逻辑方法和数学语言建构的科学或工程模型。说白了,就是用数学的方式来解释事实。举个简单的例子:你有一只铅笔,又捡了一只,一共是两只,数学模型就是1+1=2。举个复杂的例子:你在路上每周能捡到3只铅笔,数学模型就是P(X)=3/7,这个数学模型可以帮你预测明天捡到铅笔的可能性。当然解释实事的数学模型不是唯一的,比如每周捡三只铅笔的数学模型还可能是P(qt=sj|qt-1=si,qt-2=sk,…),s=0,1,也就是有两个状态的马尔可夫模型,意思就是明天是否捡到铅笔取决于前几天有没有捡到铅笔

    什么是数学建模

    数学建模就是通过计算得到的结果来解释实际问题,并接受实际的检验,来建立数学模型的全过程。

    什么是语言模型

    语言模型是根据语言客观事实而进行的语言抽象数学建模。说白了,就是找到一个数学模型,让它来解释自然语言的事实。

    业界认可的语言模型

    业界目前比较认可而且有效的语言模型是n元语法模型(n-gram model),它本质上是马尔可夫模型,简单来描述就是:一句话中下一个词的出现和最近n个词有关(包括它自身)。详细解释一下:

    如果这里的n=1时,那么最新一个词只和它自己有关,也就是它是独立的,和前面的词没关系,这叫做一元文法

    如果这里的n=2时,那么最新一个词和它前面一个词有关,比如前面的词是“我”,那么最新的这个词是“是”的概率比较高,这叫做二元文法,也叫作一阶马尔科夫链

    依次类推,工程上n=3用的是最多的,因为n越大约束信息越多,n越小可靠性更高

    n元语法模型实际上是一个概率模型,也就是出现一个词的概率是多少,或者一个句子长这个样子的概率是多少。

    这就又回到了之前文章里提到的自然语言处理研究的两大方向:基于规则、基于统计。n元语法模型显然是基于统计的方向。

    概率是如何统计的

    说到基于统计,那么就要说概率是如何估计的了,通常都是使用最大似然估计,怎么样理解“最大似然估计”,最大似然就是最最最最最相似的,那么和谁相似,和历史相似,历史是什么样的?10个词里出现过2次,所以是2/10=1/5,所以经常听说过的“最大似然估计”就是用历史出现的频率来估计概率的方法。这么说就懂了吧?

    语言模型都有哪些困难

    1. 千变万化的自然语言导致的0概率问题

    基于统计的自然语言处理需要基于大量语料库进行,而自然语言千变万化,可以理解所有词汇的笛卡尔积,数量大到无法想象,有限的语料库是难以穷举语言现象的,因此n元语法模型会出现某一句话出现的概率为0的情况,比如我这篇博客在我写出来之前概率就是0,因为我是原创。那么这个0概率的问题如何解决呢?这就是业界不断在研究的数据平滑技术,也就是通过各种数学方式来让每一句话的概率都大于0。具体方法不列举,都是玩数学的,比较简单,无非就是加个数或者减个数或者做个插值平滑一下,效果上应用在不同特点的数据上各有千秋。平滑的方法确实有效,各种自然语言工具中都实现了,直接用就好了。

    2. 特定领域的特定词概率偏大问题

    每一种领域都会有一些词汇比正常概率偏大,比如计算机领域会经常出现“性能”、“程序”等词汇,这个解决办法可以通过缓存一些刚刚出现过的词汇来提高后面出现的概率来解决。当然这里面是有很多技巧的,我们并不是认为所有出现过的词后面概率都较大,而是会考虑这些词出现的频率和规律(如:词距)来预测。

    3. 单一语言模型总会有弊端

    还是因为语料库的不足,我们会融合多种语料库,但因为不同语料库之间的差异,导致我们用单一语言模型往往不够准确,因此,有一种方法可以缓和这种不准确性,那就是把多种语言模型混到一起来计算,这其实是一种折中,这种方法low且有效。

    还有一种方法就是用多种语言模型来分别计算,最后选择熵最大的一种,这其实也是一种折中,用在哪种地方就让哪种模型生效。

    神经网络语言模型

    21世纪以来,统计学习领域无论什么都要和深度学习搭个边,毕竟计算机计算能力提升了很多,无论多深都不怕。神经网络语言模型可以看做是一种特殊的模型平滑方式,本质上还是在计算概率,只不过通过深层的学习来得到更正确的概率。

    语言模型的应用

    这几乎就是自然语言处理的应用了,有:中文分词、机器翻译、拼写纠错、语音识别、音子转换、自动文摘、问答系统、OCR等

    探究中文分词的艺术

    中文是世界语言界的一朵奇葩,它天生把词连在一起,让计算机望而却步,一句#他说的确实在理#让计算机在#的确#、#实在#、#确实#里面挣扎,但是统计自然语言处理却让计算机有了智能

    中文分词是怎么走到今天的

    话说上个世纪,中文自动分词还处于初级阶段,每句话都要到汉语词表中查找,有没有这个词?有没有这个词?所以研究集中在:怎么查找最快、最全、最准、最狠……,所以就出现了正向最大匹配法、逆向最大匹配法、双向扫描法、助词遍历法……,用新世纪比较流行的一个词来形容就是:你太low了!

    中文自动分词最难的两个问题:1)歧义消除;2)未登陆词识别。说句公道话,没有上个世纪那么low的奠定基础,也就没有这个世纪研究重点提升到这两个高级的问题

    ps:未登录词就是新词,词表里没有的词

    本世纪计算机软硬件发展迅猛,计算量存储量都不再是问题,因此基于统计学习的自动分词技术成为主流,所以就出现了各种新分词方法,也更适用于新世纪文本特点

    从n元语法模型开始说起

    上节讲到了n元语法模型,在前n-1个词出现的条件下,下一个词出现的概率是有统计规律的,这个规律为中文自动分词提供了统计学基础,所以出现了这么几种统计分词方法:N-最短路径分词法、基于n元语法模型的分词法

    N-最短路径分词法其实就是一元语法模型,每个词成为一元,独立存在,出现的概率可以基于大量语料统计得出,比如“确实”这个词出现概率的0.001(当然这是假设,别当真),我们把一句话基于词表的各种切词结果都列出来,因为字字组合可能有很多种,所以有多个候选结果,这时我们利用每个词出现的概率相乘起来,得到的最终结果,谁最大谁就最有可能是正确的,这就是N-最短路径分词法。

    这里的N的意思是说我们计算概率的时候最多只考虑前N个词,因为一个句子可能很长很长,词离得远,相关性就没有那么强了

    这里的最短路径其实是传统最短路径的一种延伸,由加权延伸到了概率乘积

    而基于n元语法模型的分词法就是在N-最短路径分词法基础上把一元模型扩展成n元模型,也就是统计出的概率不再是一个词的概率,而是基于前面n个词的条件概率

    人家基于词,我来基于字

    由字构词的分词方法出现可以说是一项突破,发明者也因此得到了各项第一和很多奖项,那么这个著名的分词法是怎么做的呢?

    每个字在词语中都有一个构词位置:词首、词中、词尾、单独构词。根据一个字属于不同的构词位置,我们设计出来一系列特征,比如:前一个词、前两个词、前面词长度、前面词词首、前面词词尾、前面词词尾加上当前的字组成的词……

    我们基于大量语料库,利用平均感知机分类器对上面特征做打分,并训练权重系数,这样得出的模型就可以用来分词了,句子右边多出来一个字,用模型计算这些特征的加权得分,得分最高的就是正确的分词方法

    分词方法纵有千万种,一定有适合你的那一个

    分词方法很多,效果上一定是有区别的,基于n元语法模型的方法的优势在于词表里已有的词的分词效果,基于字构词的方法的优势在于未登陆词的识别,因此各有千秋,你适合哪个就用哪个。

    异性相吸,优势互补

    既然两种分词各有优缺点,那么就把他们结合起来吧,来个插值法折中一下,用过的人都说好

    流行分词工具都是用的什么分词方法

    jieba中文分词

    官方描述:

    • 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG)
    • 采用了动态规划查找最大概率路径, 找出基于词频的最大切分 组合
    • 对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法

    前两句话是说它是基于词表的分词,最后一句是说它也用了由字构词,所以它结合了两种分词方法

    ik分词器

    基于词表的最短路径切词

    ltp云平台分词

    主要基于机器学习框架并部分结合词表的方法

    一篇文章读懂拿了图灵奖和诺贝尔奖的概率图模型

    概率图模型是概率论和图论的结合,经常见到的贝叶斯网络、马尔可夫模型、最大熵模型、条件随机场都属于概率图模型,这些模型有效的解决了很多实际问题,比如自然语言处理中的词性标注、实体识别等,书里的描述都公式纵横、晦涩难懂,我们不妨试试轻轻松松的来说一说概率图模型

    首先我们说说什么是图论

    能点进这篇文章说明你一定是有一定数学基础的,所以我做个比喻,你来看看是不是这么回事。糖葫芦吃过吧?几个山楂串在一根杆上,这其实就是一个图。

    稍稍正式一点说:图就是把一些孤立的点用线连起来,任何点之间都有可能连着。它区别于树,树是有父子关系,图没有。

    再深入一点点:从质上来说,图可以表达的某些事物之间的关联关系,也可以表达的是一种转化关系;从量上来说,它能表达出关联程度,也能表达出转化的可能性大小

    图论一般有什么用途呢?著名的七桥问题、四色问题、欧拉定理都是在图论基础上说事儿的

    再说说概率论

    概率论从中学到大学再到工作中都在学,它原理很简单:投个硬币出现人头的概率是1/2,最常用的就是条件概率P(B|A),联合概率P(A,B),贝叶斯公式:P(B|A)=P(A|B)P(B)/P(A),各种估计方法。

    提前解释一下概率图模型里的几个常见词汇

    贝叶斯(Bayes):无论什么理论什么模型,只要一提到他,那么里面一定是基于条件概率P(B|A)来做文章的。ps:贝叶斯老爷爷可是18世纪的人物,他的理论到现在还这么火,可见他的影响力绝不下于牛顿、爱因斯坦

    马尔可夫(Markov):无论什么理论什么模型,只要一提到他,那么里面一定有一条链式结构或过程,前n个值决定当前这个值,或者说当前这个值跟前n个值有关

    熵(entropy):熵有火字旁,本来是一个热力学术语,表示物质系统的混乱状态。延伸数学上表达的是一种不确定性。延伸到信息论上是如今计算机网络信息传输的基础理论,不确定性函数是f(p)=-logp,信息熵H(p)=-∑plogp。提到熵必须要提到信息论鼻祖香农(Shannon)

    场(field):只要在数学里见到场,它都是英文里的“域”的概念,也就是取值空间,如果说“随机场”,那么就表示一个随机变量能够赋值的全体空间

    再说概率图模型

    概率图模型一般是用图来说明,用概率来计算的。所以为了清晰的说明,我们每一种方法我尽量配个图,并配个公式。

    首先,为了脑子里有个体系,我们做一个分类,分成有向图模型和无向图模型,顾名思义,就是图里面的边是否有方向。那么什么样的模型的边有方向,而什么样的没方向呢?这个很好想到,有方向的表达的是一种推演关系,也就是在A的前提下出现了B,这种模型又叫做生成式模型。而没有方向表达的是一种“这样就对了”的关系,也就是A和B同时存在就对了,这种模型又叫做判别式模型。生成式模型一般用联合概率计算(因为我们知道A的前提了,可以算联合概率),判别式模型一般用条件概率计算(因为我们不知道前提,所以只能”假设”A条件下B的概率)。生成式模型的代表是:n元语法模型、隐马尔可夫模型、朴素贝叶斯模型等。判别式模型的代表是:最大熵模型、支持向量机、条件随机场、感知机模型等

    贝叶斯网络

    按照前面说的,提到贝叶斯就是条件概率,所以也就是生成式模型,也就是有向图模型。

    为了说明什么是贝叶斯网络,我从网上盗取一个图

    image

    图中每一个点都可能未True或False,他们的概率是已知的,比如x7的概率需要有x4和x5来决定,可能是这样的

    x4x5TF
    TT0.50.5
    TF0.40.6
    FT0.70.3
    FF0.20.8

    那么可以通过上面的贝叶斯网络来估计如果x1为False情况下x6为True的概率:

    P(x6=T|x1=F)=P(x6=T,x1=F)/P(x1=F)

    这个值继续推导,最终可以由每个节点的概率数据计算求得,这么说来,贝叶斯网络模型可以通过样本学习来估计每个节点的概率,从而达到可以预测各种问题的结果

    贝叶斯网络能够在已知有限的、不完整的、不确定信息条件下进行学习推理,所以广泛应用在故障诊断、维修决策、汉语自动分词、词义消歧等问题上

    马尔可夫模型和隐马尔可夫模型

    按照前面说的,提到马尔可夫就是一个值跟前面n个值有关,所以也就是条件概率,也就是生成式模型,也就是有向图模型。

    继续盗图

    音乐的每一个音不是随意作出来的,是根据曲子的风格、和弦、大小调式等来决定的,但是因为可选的音高有多种,也就出现了无数美妙的旋律。因为有约束,所以其实可以说新的音和前面的n个音有关,这其实是一个马尔可夫模型可以解释的事情。

    马尔可夫模型还可以看成是一个关于时间t的状态转换过程,也就是随机的有限状态机,那么状态序列的概率可以通过计算形成该序列所有状态之间转移弧上的概率乘积得出。

    如果说这个马尔可夫是两阶的,那么转移概率可能是这个样子:

    当然后面的概率只是举了个例子,这种情况由前两列决定的第三列任意值都会有一个概率

    我们通过训练样本来得出每一个概率值,这样就可以通过训练出的模型来根据前两个音是什么而预测下一个音是1、2、3、4、5任意一个的概率是多少了,也就是可以自动作曲了,当然这样做出的曲子肯定是一个无线循环的旋律,你猜猜为什么。

    那么我们再说隐马尔可夫模型,这里的“隐”指的是其中某一阶的信息我们不知道,就像是我们知道人的祖先是三叶虫,但是由三叶虫经历了怎样的演变过程才演变到人的样子我们是不知道的,我们只能通过化石资料了解分布信息,如果这类资料很多,那么就可以利用隐马尔可夫模型来建模,因为缺少的信息较多,所以这一模型的算法比较复杂,比如前向算法、后向算法之类晦涩的东西就不说了。相对于原理,我们更关注它的应用,隐马尔可夫模型广泛应用在词性标注、中文分词等,为什么能用在这两个应用上呢?仔细想一下能看得出来,比如中文分词,最初你是不知道怎么分词的,前面的词分出来了,你才之后后面的边界在哪里,但是当你后面做了分词之后还要验证前面的分词是否正确,这样前后有依赖关系,而不确定中间状态的情况最适合用隐马尔可夫模型来解释

    最大熵模型

    按照前面所说的,看到熵那么一定会用到H(p)=-∑plogp,怎么理解最大熵模型呢?我们的最终目的是想知道在某一个信息条件B下,得出某种可能的结果A的最大的概率,也就是条件概率P(A|B)最大的候选结果。因为最大熵就是不确定性最大,其实也就是条件概率最大,所以求最大的条件概率等同于求最大熵,而我们这里的熵其实是H(p)=H(A|B)=-∑p(b)p(a|b)log(p(a|b)),为了使用训练数据做估计,这里的p(a|b)可以通过训练数据的某些特征来估计,比如这些特征是fi(a,b),那么做模型训练的过程就编程了训练∑λf(a,b)中的λ参数的过程,至此就有些像机器学习的线性回归了,该怎么做就清晰了。所以其实最大熵模型就是利用熵的原理和熵的公式来用另外一种形式来描述具有概率规律的现实的

    条件随机场

    场表示取值范围,随机场表示随机变量有取值范围,也就是每个随机变量有固定的取值,条件指的是随机变量的取值由一定的条件概率决定,而这里的条件来自于我们有一些观察值,这是它区别于其他随机场的地方。条件随机场也可以看做是一个无向图模型,它特殊就特殊在给定观察序列X时某个特定的标记序列Y的概率是一个指数函数exp(∑λt+∑μs),其中t是转移函数,s是状态函数,我们需要训练的是λ和μ。条件随机场主要应用在标注和切分有序数据上,尤其在自然语言处理、生物信息学、机器视觉、网络智能等方面

    总结一下,概率图模型包括多种结合概率论和图论的模型,根据特定场景特定需求选择不同的模型,每种模型的参数都需要大量样本训练得出,每种模型都是用来根据训练出来的概率做最优结论选择的,比如根据训练出来的模型对句子做最正确的词性标注、实体标注、分词序列等,本文只是从理念上的解释和总结,真的用到某一种模型还是需要深入研究原理和公式推导以及编程实现,那就不是本文这种小篇幅能够解释的完的了,等我们后面要遇到必须用某一种模型来实现时再狠狠地深入一下。

    大话自然语言处理中的囊中取物

    大数据风靡的今天,不从里面挖出点有用的信息都不好意思见人,人工智能号称跨过奇点,统霸世界,从一句话里都识别不出一个命名实体?不会的,让我们大话自然语言处理的囊中取物,看看怎么样能让计算机像人一样看出一句话里哪个像人、哪个像物

    话说天下大事,分久必合,合久必分。

    之前谈到中文分词把文本切分成一个一个词语,现在我们要反过来,把该拼一起的词再拼到一起,找到一个命名实体,比如:“亚太经合组织”

    条件随机场的用武之地

    上回书说到,概率图模型中的条件随机场适用于在一定观测值条件下决定的随机变量有有限个取值的情况,它特殊就特殊在给定观察序列X时某个特定的标记序列Y的概率是一个指数函数exp(∑λt+∑μs),这也正符合最大熵原理。基于条件随机场的命名实体识别方法属于有监督的学习方法,需要利用已经标注好的大规模语料库进行训练,那么已经标注好的语料里面有什么样的特征能够让模型得以学习呢?

    谈命名实体的放射性

    为什么说命名实体是有放射性的呢?举个栗子:“中国积极参与亚太经合组织的活动”,这里面的“亚太经合组织”是一个命名实体,定睛一瞧,这个实体着实不凡啊,有“组织”两个字,这么说来这个实体是一种组织或机构,记住,下一次当你看到“组织”的时候和前面几个字组成的一定是一个命名实体。继续观察,在它之前辐射出了“参与”一次,经过大规模语料训练后能发现,才“参与”后面有较大概率跟着一个命名实体。继续观察,在它之后有“的活动”,那么说明前面很可能是一个组织者,组织者多半是一个命名实体。这就是基于条件随机场做命名实体识别的奥秘,这就是命名实体的放射性

    特征模板

    前面讲了放射性,那么设计特征模板就比较容易了,我们采用当前位置的前后n个位置上的字/词/字母/数字/标点等作为特征,因为是基于已经标注好的语料,所以这些特征是什么样的词性、词形都是已知的。

    特征模板的选择是和具体我们要识别的实体类别有关系的,识别人名和识别机构名用的特征模板是不一样的,因为他们的特点就不一样,事实上识别中文人名和识别英文人名用的特征模板也是不一样的,因为他们的特点就不一样

    且说命名实体

    前面讲了一揽子原理,回过头来讲讲命名实体是什么,命名实体包括:人名(政治家、艺人等)、地名(城市、州、国家、建筑等)、组织机构名、时间、数字、专有名词(电影名、书名、项目名、电话号码等)、……。其实领域很多,不同人需求不一样,关注的范围也不一样。总之不外乎命名性指称、名词性指称和代词性指称

    自古英雄周围总有谋士

    基于条件随机场的命名实体方法虽好,但如何利用好还是需要各路谋士献计献策。有的人提出通过词形上下文训练模型,也就是给定词形上下文语境中产生实体的概率;有的人提出通过词性上下文训练模型,也就是给定词性上下文语境中产生实体的概率;有的人提出通过给定实体的词形串作为实体的概率;有的人提出通过给定实体的词性串作为实体的概率;当大家发现这四点总有不足时,有谋士提出:把四个结合起来!这真是:英雄代有人才出,能摆几出摆几出啊

    语料训练那些事儿

    语料训练那些事儿,且看我机器学习教程相关文章《机器学习精简入门教程》,预知后事如何,下回我也不分解了

    让机器做词性自动标注的具体方法

    分词、命名实体识别和词性标注这三项技术如果达不到很高的水平,是难以建立起高性能的自然语言处理系统,也就难以实现高质量的聊天机器人,而词性是帮助计算机理解语言含义的关键,本节来介绍一些词性标注的具体方法

    何为词性

    常说的词性包括:名、动、形、数、量、代、副、介、连、助、叹、拟声。但自然语言处理中要分辨的词性要更多更精细,比如:区别词、方位词、成语、习用语、机构团体、时间词等,多达100多种。

    汉语词性标注最大的困难是“兼类”,也就是一个词在不同语境中有不同的词性,而且很难从形式上识别。

    词性标注过程

    为了解决词性标注无法达到100%准确的问题,词性标注一般要经过“标注”和“校验”两个过程,第一步“标注”根据规则或统计的方法做词性标注,第二步“校验”通过一致性检查和自动校对等方法来修正。

    词性标注的具体方法

    词性标注具体方法包括:基于统计模型的方法、基于规则的方法和两者结合的方法。下面我们分别来介绍。

    基于统计模型的词性标注方法

    提到基于统计模型,势必意味着我们要利用大量已经标注好的语料库来做训练,同时要先选择一个合适的训练用的数学模型,《自己动手做聊天机器人 十五-一篇文章读懂拿了图灵奖和诺贝尔奖的概率图模型》中我们介绍了概率图模型中的隐马尔科夫模型(HMM)比较适合词性标注这种基于观察序列来做标注的情形。语言模型选择好了,下面要做的就是基于语料库来训练模型参数,那么我们模型参数初值如何设置呢?这里面就有技巧了

    隐马尔可夫模型参数初始化的技巧

    模型参数初始化是在我们尚未利用语料库之前用最小的成本和最接近最优解的目标来设定初值。HMM是一种基于条件概率的生成式模型,所以模型参数是生成概率,那么我们不妨就假设每个词的生成概率就是它所有可能的词性个数的倒数,这个是计算最简单又最有可能接近最优解的生成概率了。每个词的所有可能的词性是我们已经有的词表里标记好的,这个词表的生成方法就比较简单了,我们不是有已经标注好的语料库嘛,很好统计。那么如果某个词在词表里没有呢?这时我们可以把它的生成概率初值设置为0。这就是隐马尔可夫模型参数初始化的技巧,总之原则就是用最小的成本和最接近最优解的目标来设定初值。一旦完成初始值设定后就可以利用前向后向算法进行训练了。

    基于规则的词性标注方法

    规则就是我们既定好一批搭配关系和上下文语境的规则,判断实际语境符合哪一种则按照规则来标注词性。这种方法比较古老,适合于既有规则,对于兼词的词性识别效果较好,但不适合于如今网络新词层出不穷、网络用语新规则的情况。于是乎,有人开始研究通过机器学习来自动提取规则,怎么提取呢?不是随便给一堆语料,它直接来生成规则,而是根据初始标注器标注出来的结果和人工标注的结果的差距,来生成一种修正标注的转换规则,这是一种错误驱动的学习方法。基于规则的方法还有一个好处在于:经过人工校总结出的大量有用信息可以补充和调整规则库,这是统计方法做不到的。

    统计方法和规则方法相结合的词性标注方法

    统计方法覆盖面比较广,新词老词通吃,常规非常规通吃,但对兼词、歧义等总是用经验判断,效果不好。规则方法对兼词、歧义识别比较擅长,但是规则总是覆盖不全。因此两者结合再好不过,先通过规则排歧,再通过统计标注,最后经过校对,可以得到正确的标注结果。在两者结合的词性标注方法中,有一种思路可以充分发挥两者优势,避免劣势,就是首选统计方法标注,同时计算计算它的置信度或错误率,这样来判断是否结果是否可疑,在可疑情况下采用规则方法来进行歧义消解,这样达到最佳效果。

    词性标注的校验

    做完词性标注并没有结束,需要经过校验来确定正确性以及修正结果。

    第一种校验方法就是检查词性标注的一致性。一致性指的是在所有标注的结果中,具有相同语境下同一个词的标注是否都相同,那么是什么原因导致的这种不一致呢?一种情况就是这类词就是兼类词,可能被标记为不同词性。另一种情况是非兼类词,但是由于人工校验或者其他原因导致标记为不同词性。达到100%的一致性是不可能的,所以我们需要保证一致性处于某个范围内,由于词数目较多,词性较多,一致性指标无法通过某一种计算公式来求得,因此可以基于聚类和分类的方法,根据欧式距离来定义一致性指标,并设定一个阈值,保证一致性在阈值范围内。

    第二种校验方法就是词性标注的自动校对。自动校对顾名思义就是不需要人参与,直接找出错误的标注并修正,这种方法更适用于一个词的词性标注通篇全错的情况,因为这种情况基于数据挖掘和规则学习方法来做判断会相对比较准确。通过大规模训练语料来生成词性校对决策表,然后根据这个决策表来找通篇全错的词性标注并做自动修正。

    总结

    词性标注的方法主要有基于统计和基于规则的方法,另外还包括后期校验的过程。词性标注是帮助计算机理解语言含义的关键,有了词性标注,我们才可以进一步确定句法和语义,才有可能让机器理解语言的含义,才有可能实现聊天机器人的梦想

    神奇算法之句法分析树的生成

    把一句话按照句法逻辑组织成一棵树,由人来做这件事是可行的,但是由机器来实现是不可思议的,然而算法世界就是这么神奇,把一个十分复杂的过程抽象成仅仅几步操作,甚至不足10行代码,就能让机器完成需要耗费人脑几十亿脑细胞的工作,本文我们来见识一下神奇的句法分析树生成算法

    句法分析

    先来解释一下句法分析。句法分析分为句法结构分析和依存关系分析。

    句法结构分析也就是短语结构分析,比如提取出句子中的名次短语、动词短语等,最关键的是人可以通过经验来判断的短语结构,那么怎么由机器来判断呢?

    (有关依存关系分析的内容,具体可以看《自己动手做聊天机器人 十二-教你如何利用强大的中文语言技术平台做依存句法和语义依存分析》)

    句法分析树

    样子如下:

    1
    2
    3
    -吃(v)-
    | |
    我(rr) 肉(n)

    句法结构分析基本方法

    分为基于规则的分析方法和基于统计的分析方法。基于规则的方法存在很多局限性,所以我们采取基于统计的方法,目前最成功的是基于概率上下文无关文法(PCFG)。基于PCFG分析需要有如下几个要素:终结符集合、非终结符集合、规则集。

    相对于先叙述理论再举实例的传统讲解方法,我更倾向于先给你展示一个简单的例子,先感受一下计算过程,然后再叙述理论,这样会更有趣。

    例子是这样的:我们的终结符集合是:∑={我, 吃, 肉,……},这个集合表示这三个字可以作为句法分析树的叶子节点,当然这个集合里还有很多很多的词

    我们的非终结符集合是:N={S, VP, ……},这个集合表示树的非页子节点,也就是连接多个节点表达某种关系的节点,这个集合里也是有很多元素

    我们的规则集:R={

    NN->我 0.5

    Vt->吃 1.0

    NN->肉 0.5

    VP->Vt NN 1.0

    S->NN VP 1.0

    ……

    }

    这里的句法规则符号可以参考词性标注,后面一列是模型训练出来的概率值,也就是在一个固定句法规则中NN的位置是“我”的概率是0.5,NN推出“肉”的概率是0.5,0.5+0.5=1,也就是左部相同的概率和一定是1。不知道你是否理解了这个规则的内涵

    再换一种方法解释一下,有一种句法规则是:

    1
    2
    3
    4
    5
    S——|
    | |
    NN VP
    |——|
    Vt NN

    其中NN的位置可能是“我”,也可能是“肉”,是“我”的概率是0.5,是“肉”的概率是0.5,两个概率和必为1。其中Vt的位置一定是“吃”,也就是概率是1.0……。这样一说是不是就理解了?

    规则集里实际上还有很多规则,只是列举出会用到的几个

    以上的∑、N、R都是经过机器学习训练出来的数据集及概率,具体训练方法下面我们会讲到

    那么如何根据以上的几个要素来生成句法分析树呢?

    (1)“我”

    词性是NN,推导概率是0.5,树的路径是“我”

    (2)“吃”

    词性是Vt,推导概率是1.0,树的路径是“吃”

    (3)“肉”

    词性是NN,概率是0.5,和Vt组合符合VP规则,推导概率是0.51.01.0=0.5,树的路径是“吃肉”

    NN和VP组合符合S规则,推导概率是0.50.51.0=0.25,树的路径是“我吃肉”

    所以最终的树结构是:

    1
    2
    3
    4
    5
    6
    S——|
    | |
    NN VP
    我 |——|
    Vt NN
    吃 肉

    上面的例子是比较简单的,实际的句子会更复杂,但是都是通过这样的动态规划算法完成的

    提到动态规划算法,就少不了“选择”的过程,一句话的句法结构树可能有多种,我们只选择概率最大的那一种作为句子的最佳结构,这也是“基于概率”上下文无关文法的名字起源。

    上面的计算过程总结起来就是:设W={ω1ω2ω3……}表示一个句子,其中的ω表示一个词(word),利用动态规划算法计算非终结符A推导出W中子串ωiωi+1ωi+2……ωj的概率,假设概率为αij(A),那么有如下递归公式:

    αij(A)=P(A->ωi)

    αij(A)=∑∑P(A->BC)αik(B)α(k+1)j(C)

    以上两个式子好好理解一下其实就是上面“我吃肉”的计算过程

    以上过程理解了之后你一定会问,这里面最关键的的非终结符、终结符以及规则集是怎么得来的,概率又是怎么确定的?下面我们就来说明

    句法规则提取方法与PCFG的概率参数估计

    这部分就是机器学习的知识了,有关机器学习可以参考《机器学习教程》

    首先我们需要大量的树库,也就是训练数据。然后我们把树库中的句法规则提取出来生成我们想要的结构形式,并进行合并、归纳等处理,最终得到上面∑、N、R的样子。其中的概率参数计算方法是这样的:

    先给定参数为一个随机初始值,然后采用EM迭代算法,不断训练数据,并计算每条规则使用次数作为最大似然计算得到概率的估值,这样不断迭代更新概率,最终得出的概率可以认为是符合最大似然估计的精确值。

    总结一下

    句法分析树生成算法是基于统计学习的原理,根据大量标注的语料库(树库),通过机器学习算法得出非终结符、终结符、规则集及其概率参数,然后利用动态规划算法生成每一句话的句法分析树,在句法分析树生成过程中如果遇到多种树结构,选择概率最大的那一种作为最佳句子结构

    机器人是怎么理解“日后再说”的

    日后再说这个成语到了当代可以说含义十分深刻,你懂的,但是如何让计算机懂得可能有两种含义的一个词到底是想表达哪个含义呢?这在自然语言处理中叫做词义消歧,从本节开始我们从基本的结构分析跨入语义分析,开始让计算机对语言做深层次的理解

    词义消歧

    词义消歧是句子和篇章语义理解的基础,是必须解决的问题。任何一种语言都有大量具有多种含义的词汇,中文的“日”,英文的“bank”,法语的“prendre”……。

    词义消歧可以通过机器学习的方法来解决。谈到机器学习就会分成有监督和无监督的机器学习。词义消歧有监督的机器学习方法也就是分类算法,即判断词义所属的分类。词义消歧无监督的机器学习方法也就是聚类算法,把词义聚成多类,每一类是一种含义。

    有监督的词义消歧方法

    基于互信息的词义消歧方法

    这个方法的名字不好理解,但是原理却非常简单:用两种语言对照着看,比如:中文“打人”对应英文“beat a man”,而中文“打酱油”对应英文“buy some sauce”。这样就知道当上下文语境里有“人”的时候“打”的含义是beat,当上下文语境里有“酱油”的时候“打”的含义是buy。按照这种思路,基于大量中英文对照的语料库训练出来的模型就可以用来做词义消歧了,这种方法就叫做基于“互信息”的词义消歧方法。讲到“互信息”还要说一下它的起源,它来源于信息论,表达的是一个随机变量中包含另一个随机变量的信息量(也就是英文信息中包含中文信息的信息量),假设两个随机变量X、Y的概率分别是p(x), p(y),它们的联合分布概率是p(x,y),那么互信息计算公式是:

    1
    I(X; Y) = ∑∑p(x,y)log(p(x,y)/(p(x)p(y)))

    以上公式是怎么推导出来的呢?比较简单,“互信息”可以理解为一个随机变量由于已知另一个随机变量而减少的不确定性(也就是理解中文时由于已知了英文的含义而让中文理解更确定了),因为“不确定性”就是熵所表达的含义,所以:

    1
    I(X; Y) = H(X) - H(X|Y)

    等式后面经过不断推导就可以得出上面的公式,对具体推导过程感兴趣可以百度一下。

    那么我们在对语料不断迭代训练过程中I(X; Y)是不断减小的,算法终止的条件就是I(X; Y)不再减小。

    基于互信息的词义消歧方法自然对机器翻译系统的效果是最好的,但它的缺点是:双语语料有限,多种语言能识别出歧义的情况也是有限的(比如中英文同一个词都有歧义就不行了)。

    基于贝叶斯分类器的消歧方法

    提到贝叶斯那么一定少不了条件概率,这里的条件指的就是上下文语境这个条件,任何多义词的含义都是跟上下文语境相关的。假设语境(context)记作c,语义(semantic)记作s,多义词(word)记作w,那么我要计算的就是多义词w在语境c下具有语义s的概率,即:

    p(s|c)

    那么根据贝叶斯公式:

    p(s|c) = p(c|s)p(s)/p(c)

    我要计算的就是p(s|c)中s取某一个语义的最大概率,因为p(c)是既定的,所以只考虑分子的最大值:

    s的估计=max(p(c|s)p(s))

    因为语境c在自然语言处理中必须通过词来表达,也就是由多个v(词)组成,那么也就是计算:

    max(p(s)∏p(v|s))

    下面就是训练的过程了:

    p(s)表达的是多义词w的某个语义s的概率,可以统计大量语料通过最大似然估计求得:

    p(s) = N(s)/N(w)

    p(v|s)表达的是多义词w的某个语义s的条件下出现词v的概率,可以统计大量语料通过最大似然估计求得:

    p(v|s) = N(v, s)/N(s)

    训练出p(s)和p(v|s)之后我们对一个多义词w消歧的过程就是计算(p(c|s)p(s))的最大概率的过程

    无监督的词义消歧方法

    完全无监督的词义消歧是不可能的,因为没有标注是无法定义是什么词义的,但是可以通过无监督的方法来做词义辨识。无监督的词义辨识其实也是一种贝叶斯分类器,和上面讲到的贝叶斯分类器消歧方法不同在于:这里的参数估计不是基于有标注的训练预料,而是先随机初始化参数p(v|s),然后根据EM算法重新估计这个概率值,也就是对w的每一个上下文c计算p(c|s),这样可以得到真实数据的似然值,回过来再重新估计p(v|s),重新计算似然值,这样不断迭代不断更新模型参数,最终得到分类模型,可以对词进行分类,那么有歧义的词在不同语境中会被分到不同的类别里。

    仔细思考一下这种方法,其实是基于单语言的上下文向量的,那么我们进一步思考下一话题,如果一个新的语境没有训练模型中一样的向量怎么来识别语义?

    这里就涉及到向量相似性的概念了,我们可以通过计算两个向量之间夹角余弦值来比较相似性,即:

    cos(a,b) = ∑ab/sqrt(∑a^2∑b^2)

    机器人是怎么理解“日后再说”的

    回到最初的话题,怎么让机器人理解“日后再说”,这本质上是一个词义消歧的问题,假设我们利用无监督的方法来辨识这个词义,那么就让机器人“阅读”大量语料进行“学习”,生成语义辨识模型,这样当它听到这样一则对话时:

    有一位老嫖客去找小姐,小姐问他什么时候结账啊。嫖客说:“钱的事情日后再说。”就开始了,完事后,小姐对嫖客说:“给钱吧。”嫖客懵了,说:“不是说日后再说吗?”小姐说:“是啊,你现在不是已经日后了吗?”

    辨识了这里的“日后再说”的词义后,它会心的笑了

    语义角色标注的基本方法

    浅层语义标注是行之有效的语言分析方法,基于语义角色的浅层分析方法可以描述句子中语义角色之间的关系,是语义分析的重要方法,也是篇章分析的基础,本节介绍基于机器学习的语义角色标注方法

    语义角色

    举个栗子:“我昨天吃了一块肉”,按照常规理解“我吃肉”应该是句子的核心,但是对于机器来说“我吃肉”实际上已经丢失了非常多的重要信息,没有了时间,没有了数量。为了让机器记录并提取出这些重要信息,句子的核心并不是“我吃肉”,而是以谓词“吃”为核心的全部信息。

    “吃”是谓词,“我”是施事者,“肉”是受事者,“昨天”是事情发生的时间,“一块”是数量。语义角色标注就是要分析出这一些角色信息,从而可以让计算机提取出重要的结构化信息,来“理解”语言的含义。

    语义角色标注的基本方法

    语义角色标注需要依赖句法分析的结果进行,因为句法分析包括短语结构分析、浅层句法分析、依存关系分析,所以语义角色标注也分为:基于短语结构树的语义角色标注方法、基于浅层句法分析结果的语义角色标注方法、基于依存句法分析结果的语义角色标注方法。但无论哪种方法,过程都是:

    句法分析->候选论元剪除->论元识别->论元标注->语义角色标注结果

    其中论元剪除就是在较多候选项中去掉肯定不是论元的部分

    其中论元识别是一个二值分类问题,即:是论元和不是论元

    其中论元标注是一个多值分类问题

    下面分别针对三种方法分别说明这几个过程的具体方法

    基于短语结构树的语义角色标注方法

    短语结构树是这样的结构:

    1
    2
    3
    4
    5
    6
    S——|
    | |
    NN VP
    我 |——|
    Vt NN
    吃 肉

    短语结构树里面已经表达了一种结构关系,因此语义角色标注的过程就是依赖于这个结构关系来设计的一种复杂策略,策略的内容随着语言结构的复杂而复杂化,因此我们举几个简单的策略来说明。

    首先我们分析论元剪除的策略:

    因为语义角色是以谓词为中心的,因此在短语结构树中我们也以谓词所在的节点为中心,先平行分析,比如这里的“吃”是谓词,和他并列的是“肉”,明显“肉”是受事者,那么设计什么样的策略能使得它成为候选论元呢?我们知道如果“肉”存在一个短语结构的话,那么一定会多处一个树分支,那么“肉”和“吃”一定不会在树的同一层,因此我们设计这样的策略来保证“肉”被选为候选论元:如果当前节点的兄弟节点和当前节点不是句法结构的并列关系,那么将它作为候选论元。当然还有其他策略不需要记得很清楚,现用现查就行了,但它的精髓就是基于短语结构树的结构特点来设计策略的。

    然后就是论元识别过程了。论元识别是一个二值分类问题,因此一定是基于标注的语料库做机器学习的,机器学习的二值分类方法都是固定的,唯一的区别就是特征的设计,这里面一般设计如下特征效果比较好:谓词本身、短语结构树路径、短语类型、论元在谓词的位置、谓词语态、论元中心词、从属类别、论元第一个词和最后一个词、组合特征。

    论元识别之后就是论元标注过程了。这又是一个利用机器学习的多值分类器进行的,具体方法不再赘述。

    基于依存句法分析结果和基于语块的语义角色标注方法

    这两种语义角色标注方法和基于短语结构树的语义角色标注方法的主要区别在于论元剪除的过程,原因就是他们基于的句法结构不同。

    基于依存句法分析结果的语义角色标注方法会基于依存句法直接提取出谓词-论元关系,这和依存关系的表述是很接近的,因此剪除策略的设计也就比较简单:以谓词作为当前节点,当前节点所有子节点都是候选论元,将当前节点的父节点作为当前节点重复以上过程直至到根节点为止。

    基于依存句法分析结果的语义角色标注方法中的论元识别算法的特征设计也稍有不同,多了有关父子节点的一些特征。

    有了以上几种语义角色标注方法一定会各有优缺点,因此就有人想到了多种方法相融合的方法,融合的方式可以是:加权求和、插值……,最终效果肯定是更好,就不多说了。

    多说几句

    语义角色标注当前还是不是非常有效,原因有诸多方面,比如:依赖于句法分析的准确性、领域适应能力差。因此不断有新方法来解决这些问题,比如说可以利用双语平行语料来弥补准确性的问题,中文不行英文来,英文不行法语来,反正多多益善,这确实有助于改进效果,但是成本提高了许多。语义角色标注还有一段相当长的路要走,希望学术界研究能不断开花结果吧

    比TF-IDF更好的隐含语义索引模型是个什么鬼

    我曾经的一篇文章曾说到0字节存储海量语料资源,那么从海量语料资源中找寻信息需要依赖于信息检索的方法,信息检索无论是谷歌还是百度都离不开TF-IDF算法,但TF-IDF是万能的吗?并不是,它简单有效但缺乏语义特征,本节介绍比TF-IDF还牛逼的含有语义特征的信息检索方法

    TF-IDF

    TF(term frequency),表示一个词在一个文档中出现的频率;IDF(inverse document frequency),表示一个词出现在多少个文档中。

    它的思路是这样的:同一个词在短文档中出现的次数和在长文档中出现的次数一样多时,对于短文档价值更大;一个出现概率很低的词一旦出现在文档中,其价值应该大于其他普遍出现的词。

    这在信息检索领域的向量模型中做相似度计算非常有效,屡试不爽,曾经是google老大哥发家的必杀技。但是在开发聊天机器人这个事情上看到了它的软肋,那就是它只是考虑独立的词上的事情,并没有任何语义信息在里面,因此我们需要选择加入了语义特征的更有效的信息检索模型。

    隐含语义索引模型

    在TF-IDF模型中,所有词构成一个高维的语义空间,每个文档在这个空间中被映射为一个点,这种方法维数一般比较高而且每个词作为一维割裂了词与词之间的关系。所以为了解决这个问题,我们要把词和文档同等对待,构造一个维数不高的语义空间,每个词和每个文档都是被映射到这个空间中的一个点。用数学来表示这个思想就是说,我们考察的概率即包括文档的概率,也包括词的概率,以及他们的联合概率。

    为了加入语义方面的信息,我们设计一个假想的隐含类包括在文档和词之间,具体思路是这样的:

    (1)选择一个文档的概率是p(d);

    (2)找到一个隐含类的概率是p(z|d);

    (3)生成一个词w的概率为p(w|z);

    以上是假设的条件概率,我们根据观测数据能估计出来的是p(d, w)联合概率,这里面的z是一个隐含变量,表达的是一种语义特征。那么我们要做的就是利用p(d, w)来估计p(d)、p(z|d)和p(w|z),最终根据p(d)、p(z|d)和p(w|z)来求得更精确的p(w, d),即词与文档之间的相关度。

    为了做更精确的估计,设计优化的目标函数是对数似然函数:

    L=∑∑n(d, w) log P(d, w)

    那么如何来通过机器学习训练这些概率呢?首先我们知道:

    p(d, w) = p(d) × p(w|d)

    p(w|d) = ∑p(w|z)p(z|d)

    同时又有:

    p(z|d) = p(z)p(d|z)/∑p(z)p(d|z)

    那么

    p(d, w) =p(d)×∑p(w|z) p(z)p(d|z)/∑p(z)p(d|z)=∑p(z)×p(w|z)×p(d|z)

    下面我们采取EM算法,EM算法的精髓就是按照最大似然的原理,先随便拍一个分布参数,让每个人都根据分布归类到某一部分,然后根据这些归类来重新统计数目,按照最大似然估计分布参数,然后再重新归类、调参、估计、归类、调参、估计,最终得出最优解

    那么我们要把每一个训练数据做归类,即p(z|d,w),那么这个概率值怎么计算呢?

    我们先拍一个p(z)、p(d|z)、p(w|z)

    然后根据

    p(z|d,w)=p(z)p(d|z)p(w|z)/∑p(z)p(d|z)p(w|z),其中分子是一个z,分母是所有的z的和

    这样计算出来的值是p(z|d,w)的最大似然估计的概率估计(这是E过程)

    然后根据这个估计来对每一个训练样本做归类

    根据归类好的数据统计出n(d,w)

    然后我再根据以下公式来更新参数

    p(z) = 1/R ∑n(d,w)p(z|d,w)

    p(d|z)=∑n(d,w)p(z|d,w) / ∑n(d,w)p(z|d,w),其中分子是一个d的和,分母是所有的d的和,这样计算出来的值是p(d|z)的最大似然估计

    p(w|z)=∑n(d,w)p(z|d,w) / ∑n(d,w)p(z|d,w),其中分子是一个w的和,分母是所有的w的和,这样计算出来的值是p(w|z)的最大似然估计

    最后重新计算p(z|d,w):

    p(z|d,w)=p(z)p(d|z)p(w|z)/∑p(z)p(d|z)p(w|z)

    这是M的过程

    不断重复上面EM的过程使得对数似然函数最大:

    L=∑∑n(d, w) log P(d, w)

    通过以上迭代就能得出最终的p(w, d),即词与文档之间的相关度,后面就是利用相关度做检索的过程了

    为了得到词词之间的相关度,我们用p(w, d)乘以它的转置,即

    p(w,w) = p(w,d)×trans(p(w,d))

    当用户查询query的关键词构成词向量Wq, 而文档d表示成词向量Wd,那么query和文档d的相关度就是:

    R(query, d) = Wq×p(w,w)×Wd

    这样把所有文档算出来的相关度从大到小排序就是搜索的排序结果

    总结

    综上就是隐含语义索引模型的内容,相比TF-IDF来说它加进了语义方面的信息、考虑了词与词之间的关系,是根据语义做信息检索的方法,更适合于研发聊天机器人做语料训练和分析,而TF-IDF更适合于完全基于独立的词的信息检索,更适合于纯文本搜索引擎

    神奇算法之人工神经网络

    深度学习是机器学习中较为流行的一种,而深度学习的基础是人工神经网络,那么人工神经网络的功能是不是像它的名字一样神奇呢?答案是肯定的,让我们一起见证一下这一神奇算法

    人工神经网络

    人工神经网络是借鉴了生物神经网络的工作原理形成的一种数学模型,有关人工神经网络的原理、公式推导以及训练过程请见我的文章《机器学习教程 十二-神经网络模型的原理》

    神奇用法之一

    我们这样来设计我们的神经网络:由n个输入特征得出与输入特征几乎相同的n个结果,这样训练出的隐藏层可以得到意想不到的信息。

    比如,在信息检索领域,我们需要通过模型训练来得出合理的排序模型,那么输入的特征可能有:文档质量、文档点击历史、文档前链数目、文档锚文本信息……,为了能找出这些特征中隐藏的信息,我们把隐藏层的神经元数目设置的少于输入特征的数目,经过大量样本的训练出能还原原始特征的模型,这样相当于我们用少于输入特征数目的信息还原出了全部特征,表面上是一种压缩,实际上通过这种方式就可以发现某些特征之间存在隐含的相关性,或者有某种特殊的关系。

    同样的,我们还可以让隐藏层中的神经元数目多余输入特征的数目,这样经过训练得出的模型还可以展示出特征之间某种细节上的关联,比如我们对图像识别做这样的模型训练,在得出的隐藏层中能展示出多种特征之间的细节信息,如鼻子一定在嘴和眼睛中间。

    这种让输出和输入一致的用法就是传说中的自编码算法。

    神奇用法之二

    人工神经网络模型通过多层神经元结构建立而成,每一层可以抽象为一种思维过程,经过多层思考,最终得出结论。举一个实际的例子:识别美女图片

    按照人的思维过程,识别美女图片要经过这样的判断:1)图片类别(人物、风景……);2)图片人物性别(男、女、其他……);3)相貌如何(美女、恐龙、5分……)

    那么在人工神经网络中,这个思考过程可以抽象成多个层次的计算:第一层计算提取图片中有关类别的特征,比如是否有形如耳鼻口手的元素,是否有形如蓝天白云绿草地的元素;第二层提取是否有胡须、胸部、长发以及面部特征等来判断性别;第三层提取五官、肤质、衣着等信息来确定颜值。为了让神经网络每一层有每一层专门要做的事情,需要在每一层的神经元中添加特殊的约束条件才能做到。人类的大脑是经过上亿年进化而成的,它的功能深不可及,某些效率也极高,而计算机在某些方面效率比人脑要高很多,两种结合起来一切皆有可能。

    这种通过很多层提取特定特征来做机器学习的方法就是传说中的深度学习。

    神奇用法之三

    讲述第三种用法之前我们先讲一下什么是卷积运算。卷积英文是convolution(英文含义是:盘绕、弯曲、错综复杂),数学表达是:

    数学上不好理解,我们可以通俗点来讲:卷积就相当于在一定范围内做平移并求平均值。比如说回声可以理解为原始声音的卷积结果,因为回声是原始声音经过很多物体反射回来声音揉在一起。再比如说回声可以理解为把信号分解成无穷多的冲击信号,然后再进行冲击响应的叠加。再比如说把一张图像做卷积运算,并把计算结果替换原来的像素点,可以实现一种特殊的模糊,这种模糊其实是一种新的特征提取,提取的特征就是图像的纹路。总之卷积就是先打乱,再叠加。

    下面我们在看上面的积分公式,需要注意的是这里是对τ积分,不是对x积分。也就是说对于固定的x,找到x附近的所有变量,求两个函数的乘积,并求和。

    下面回归正题,在神经网络里面,我们设计每个神经元计算输出的公式是卷积公式,这样相当于神经网络的每一层都会输出一种更高级的特征,比如说形状、脸部轮廓等。这种神经网络叫做卷积神经网络。

    继续深入主题,在自然语言中,我们知道较近的上下文词语之间存在一定的相关性,由于标点、特殊词等的分隔使得在传统自然语言处理中会脱离词与词之间的关联,结果丢失了一部分重要信息,利用卷积神经网络完全可以做多元(n-gram)的计算,不会损失自然语言中的临近词的相关性信息。这种方法对于语义分析、语义聚类等都有非常好的效果。

    这种神奇用法就是传说中的CNN

    总结

    神经网络因为其层次和扩展性的强大,有着非常多的神奇用法和非常广泛的应用,因为希望聊天机器人能够具有智能,就不得不寻找能够承载智能的方法,神经网络是其中一个,沿着这个网络,让我们继续探索。

    用CNN做深度学习

    自动问答系统中深度学习的应用较多是RNN,这归因于它天然利用时序建模。俗话说知己知彼百战不殆,为了理解RNN,我们先来了解一下CNN,通过手写数字识别案例来感受一下CNN最擅长的局部感知能力

    卷积神经网络(CNN)

    卷积神经网络(Convolutional Neural Network,CNN)是将二维离散卷积运算和人工神经网络相结合的一种深度神经网络。它的特点是可以自动提取特征。有关卷积神经网络的数学原理和训练过程请见我的另一篇文章《机器学习教程 十五-细解卷积神经网络》。

    手写数字识别

    为了试验,我们直接采用http://yann.lecun.com/exdb/mnist/中的手写数据集,下载到的手写数据集数据文件是用二进制以像素为单位保存的几万张图片文件,通过我的github项目https://github.com/warmheartli/ChatBotCourse中的read_images.c把图片打印出来是像下面这样的输出:

    具体文件格式和打印方式请见我的另一篇基于简单的softmax模型的机器学习算法文章《机器学习教程 十四-利用tensorflow做手写数字识别》中的讲解

    多层卷积网络设计

    为了对mnist手写数据集做训练,我们设计这样的多层卷积网络:

    第一层由一个卷积和一个max pooling完成,其中卷积运算的“视野”是5×5的像素范围,卷积使用1步长、0边距的模板(保证输入输出是同一个大小),1个输入通道(因为图片是灰度的,单色),32个输出通道(也就是设计32个特征)。由于我们通过上面read_images.c的打印可以看到每张图片都是28×28像素,那么第一次卷积输出也是28×28大小。max pooling采用2×2大小的模板,那么池化后输出的尺寸就是14×14,因为一共有32个通道,所以一张图片的输出一共是14×14×32=6272像素

    第二层同样由一个卷积和一个max pooling完成,和第一层不同的是输入通道有32个(对应第一层的32个特征),输出通道我们设计64个(即输出64个特征),因为这一层的输入是每张大小14×14,所以这一个卷积层输出也是14×14,再经过这一层max pooling,输出大小就是7×7,那么一共输出像素就是7×7×64=3136

    第三层是一个密集连接层,我们设计一个有1024个神经元的全连接层,这样就相当于第二层输出的7×7×64个值都作为这1024个神经元的输入

    为了让算法更“智能”,我们把这些神经元的激活函数设计为ReLu函数,即如下图像中的蓝色(其中绿色是它的平滑版g(x)=log(1+e^x)):

    最终的输出层,我们以第三层的1024个输出为输入,设计一个softmax层,输出10个概率值

    tensorflow代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    # coding:utf-8
    importsys
    reload(sys)
    sys.setdefaultencoding("utf-8")
    fromtensorflow.examples.tutorials.mnistimportinput_data
    importtensorflowastf
    flags = tf.app.flags
    FLAGS = flags.FLAGS
    flags.DEFINE_string('data_dir','./','Directory for storing data')
    mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)
    # 初始化生成随机的权重(变量),避免神经元输出恒为0
    defweight_variable(shape):
    # 以正态分布生成随机值
    initial = tf.truncated_normal(shape, stddev=0.1)
    returntf.Variable(initial)
    # 初始化生成随机的偏置项(常量),避免神经元输出恒为0
    defbias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    returntf.Variable(initial)
    # 卷积采用1步长,0边距,保证输入输出大小相同
    defconv2d(x, W):
    returntf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
    # 池化采用2×2模板
    defmax_pool_2x2(x):
    returntf.nn.max_pool(x, ksize=[1,2,2,1],
    strides=[1,2,2,1], padding='SAME')
    # 28*28=784
    x = tf.placeholder(tf.float32, [None,784])
    # 输出类别共10个:0-9
    y_ = tf.placeholder("float", [None,10])
    # 第一层卷积权重,视野是5*5,输入通道1个,输出通道32个
    W_conv1 = weight_variable([5,5,1,32])
    # 第一层卷积偏置项有32个
    b_conv1 = bias_variable([32])
    # 把x变成4d向量,第二维和第三维是图像尺寸,第四维是颜色通道数1
    x_image = tf.reshape(x, [-1,28,28,1])
    h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
    h_pool1 = max_pool_2x2(h_conv1)
    # 第二层卷积权重,视野是5*5,输入通道32个,输出通道64个
    W_conv2 = weight_variable([5,5,32,64])
    # 第二层卷积偏置项有64个
    b_conv2 = bias_variable([64])
    h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)
    # 第二层池化后尺寸编程7*7,第三层是全连接,输入是64个通道,输出是1024个神经元
    W_fc1 = weight_variable([7*7*64,1024])
    # 第三层全连接偏置项有1024个
    b_fc1 = bias_variable([1024])
    h_pool2_flat = tf.reshape(h_pool2, [-1,7*7*64])
    h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
    # 按float做dropout,以减少过拟合
    keep_prob = tf.placeholder("float")
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
    # 最后的softmax层生成10种分类
    W_fc2 = weight_variable([1024,10])
    b_fc2 = bias_variable([10])
    y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
    cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
    # Adam优化器来做梯度最速下降
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))
    sess = tf.InteractiveSession()
    sess.run(tf.initialize_all_variables())
    foriinrange(20000):
    batch = mnist.train.next_batch(50)
    ifi%100==0:
    train_accuracy = accuracy.eval(feed_dict={
    x:batch[0], y_: batch[1], keep_prob:1.0})
    print"step %d, training accuracy %g"%(i, train_accuracy)
    train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob:0.5})
    print"test accuracy %g"%accuracy.eval(feed_dict={
    x: mnist.test.images, y_: mnist.test.labels, keep_prob:1.0})

    输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    [root@centos $] python digital_recognition_cnn.py
    Extracting ./train-images-idx3-ubyte.gz
    Extracting ./train-labels-idx1-ubyte.gz
    Extracting ./t10k-images-idx3-ubyte.gz
    Extracting ./t10k-labels-idx1-ubyte.gz
    step 0, training accuracy 0.14
    step 100, training accuracy 0.86
    step 200, training accuracy 0.9
    step 300, training accuracy 0.86
    step 400, training accuracy 1
    step 500, training accuracy 0.92
    step 600, training accuracy 1
    step 700, training accuracy 0.96
    step 800, training accuracy 0.88
    step 900, training accuracy 1
    step 1000, training accuracy 0.96
    step 1100, training accuracy 0.98
    step 1200, training accuracy 0.94
    step 1300, training accuracy 0.92
    step 1400, training accuracy 0.98
    ……

    最终准确率大概能达到99.2%

    将深度学习应用到NLP

    由于语言相比于语音、图像来说,是一种更高层的抽象,因此不是那么适合于深度学习,但是经过人类不断探索,也发现无论多么高层的抽象总是能通过更多底层基础的累积而碰触的到,本文介绍如何将深度学习应用到NLP所必须的底层基础

    词向量

    自然语言需要数学化才能够被计算机认识和计算。数学化的方法有很多,最简单的方法是为每个词分配一个编号,这种方法已经有多种应用,但是依然存在一个缺点:不能表示词与词的关系。

    词向量是这样的一种向量[0.1, -3.31, 83.37, 93.0, -18.37, ……],每一个词对应一个向量,词义相近的词,他们的词向量距离也会越近(欧氏距离、夹角余弦)

    词向量有一个优点,就是维度一般较低,一般是50维或100维,这样可以避免维度灾难,也更容易使用深度学习

    词向量如何训练得出呢?

    首先要了解一下语言模型,语言模型相关的内容请见我另外一篇文章《自己动手做聊天机器人 十三-把语言模型探究到底》。语言模型表达的实际就是已知前n-1个词的前提下,预测第n个词的概率。

    词向量的训练是一种无监督学习,也就是没有标注数据,给我n篇文章,我就可以训练出词向量。

    基于三层神经网络构建n-gram语言模型(词向量顺带着就算出来了)的基本思路:

    最下面的w是词,其上面的C(w)是词向量,词向量一层也就是神经网络的输入层(第一层),这个输入层是一个(n-1)×m的矩阵,其中n-1是词向量数目,m是词向量维度

    第二层(隐藏层)是就是普通的神经网络,以H为权重,以tanh为激活函数

    第三层(输出层)有|V|个节点,|V|就是词表的大小,输出以U为权重,以softmax作为激活函数以实现归一化,最终就是输出可能是某个词的概率。

    另外,神经网络中有一个技巧就是增加一个从输入层到输出层的直连边(线性变换),这样可以提升模型效果,这个变换矩阵设为W

    假设C(w)就是输入的x,那么y的计算公式就是y = b + Wx + Utanh(d+Hx)

    这个模型里面需要训练的有这么几个变量:C、H、U、W。利用梯度下降法训练之后得出的C就是生成词向量所用的矩阵,C(w)表示的就是我们需要的词向量

    上面是讲解词向量如何“顺带”训练出来的,然而真正有用的地方在于这个词向量如何进一步应用。

    词向量的应用

    第一种应用是找同义词。具体应用案例就是google的word2vec工具,通过训练好的词向量,指定一个词,可以返回和它cos距离最相近的词并排序。

    第二种应用是词性标注和语义角色标注任务。具体使用方法是:把词向量作为神经网络的输入层,通过前馈网络和卷积网络完成。

    第三种应用是句法分析和情感分析任务。具体使用方法是:把词向量作为递归神经网络的输入。

    第四种应用是命名实体识别和短语识别。具体使用方法是:把词向量作为扩展特征使用。

    另外词向量有一个非常特别的现象:C(king)-C(queue)≈C(man)-C(woman),这里的减法就是向量逐维相减,换个表达方式就是:C(king)-C(man)+C(woman)和它最相近的向量就是C(queue),这里面的原理其实就是:语义空间中的线性关系。基于这个结论相信会有更多奇妙的功能出现。

    google的文本挖掘深度学习工具word2vec的实现原理

    词向量是将深度学习应用到NLP的根基,word2vec是如今使用最广泛最简单有效的词向量训练工具,那么它的实现原理是怎样的呢?本文将从原理出发来介绍word2vec

    你是如何记住一款车的

    问你这样一个问题:如果你大脑有很多记忆单元,让你记住一款白色奥迪Q7运动型轿车,你会用几个记忆单元?你也许会用一个记忆单元,因为这样最节省你的大脑。那么我们再让你记住一款小型灰色雷克萨斯,你会怎么办?显然你会用另外一个记忆单元来记住它。那么如果让你记住所有的车,你要耗费的记忆单元就不再是那么少了,这种表示方法叫做localist representation。这时你可能会换另外一种思路:我们用几个记忆单元来分别识别大小、颜色、品牌等基础信息,这样通过这几个记忆单元的输出,我们就可以表示出所有的车型了。这种表示方法叫做distributed representation,词向量就是一种用distributed representation表示的向量

    localist representation与distributed representation

    localist representation中文释义是稀疏表达,典型的案例就是one hot vector,也就是这样的一种向量表示:

    [1, 0, 0, 0, 0, 0……]表示成年男子

    [0, 1, 0, 0, 0, 0……]表示成年女子

    [0, 0, 1, 0, 0, 0……]表示老爷爷

    [0, 0, 0, 1, 0, 0……]表示老奶奶

    [0, 0, 0, 0, 1, 0……]表示男婴

    [0, 0, 0, 0, 0, 1……]表示女婴

    ……

    每一类型用向量中的一维来表示

    而distributed representation中文释义是分布式表达,上面的表达方式可以改成:

    性别 老年 成年 婴儿

    [1, 0, 1, 0]表示成年男子

    [0, 0, 1, 0]表示成年女子

    [1, 1, 0, 0]表示老爷爷

    [0, 1, 0, 0]表示老奶奶

    [1, 0, 0, 1]表示男婴

    [0, 0, 0, 1]表示女婴

    如果我们想表达男童和女童,只需要增加一个特征维度即可

    word embedding

    翻译成中文叫做词嵌入,这里的embedding来源于范畴论,在范畴论中称为morphism(态射),态射表示两个数学结构中保持结构的一种过程抽象,比如“函数”、“映射”,他们都是表示一个域和另一个域之间的某种关系。

    范畴论中的嵌入(态射)是要保持结构的,而word embedding表示的是一种“降维”的嵌入,通过降维避免维度灾难,降低计算复杂度,从而更易于在深度学习中应用。

    理解了distributed representation和word embedding的概念,我们就初步了解了word2vec的本质,它其实是通过distributed representation的表达方式来表示词,而且通过降维的word embedding来减少计算量的一种方法

    word2vec中的神经网络

    word2vec中做训练主要使用的是神经概率语言模型,这需要掌握一些基础知识,否则下面的内容比较难理解,关于神经网络训练词向量的基础知识我在《自己动手做聊天机器人 二十四-将深度学习应用到NLP》中有讲解,可以参考,这里不再赘述。

    在word2vec中使用的最重要的两个模型是CBOW和Skip-gram模型,下面我们分别来介绍这两种模型

    CBOW模型

    CBOW全称是Continuous Bag-of-Words Model,是在已知当前词的上下文的前提下预测当前词

    CBOW模型的神经网络结构设计如下:

    输入层:词w的上下文一共2c个词的词向量

    投影层:将输入层的2c个向量做求和累加

    输出层:一个霍夫曼树,其中叶子节点是语料中出现过的词,权重是出现的次数

    我们发现这一设计相比《自己动手做聊天机器人 二十四-将深度学习应用到NLP》中讲到的神经网络模型把首尾相接改成了求和累加,这样减少了维度;去掉了隐藏层,这样减少了计算量;输出层由softmax归一化运算改成了霍夫曼树;这一系列修改对训练的性能有很大提升,而效果不减,这是独到之处。

    基于霍夫曼树的Hierarchical Softmax技术

    上面的CBOW输出层为什么要建成一个霍夫曼树呢?因为我们是要基于训练语料得到每一个可能的w的概率。那么具体怎么得到呢?我们先来看一下这个霍夫曼树的例子:

    在这个霍夫曼树中,我们以词足球为例,走过的路径图上容易看到,其中非根节点上的θ表示待训练的参数向量,也就是要达到这种效果:当在投射层产出了一个新的向量x,那么我通过逻辑回归公式:

    σ(xTθ) = 1/(1+e^(-xTθ))

    就可以得出在每一层被分到左节点(1)还是右节点(0)的概率分别是

    p(d|x,θ) = 1-σ(xTθ)

    p(d|x,θ) = σ(xTθ)

    那么就有:

    p(足球|Context(足球)) = ∏ p(d|x,θ)

    现在模型已经有了,下面就是通过语料来训练v(Context(w))、x和θ的过程了

    我们以对数似然函数为优化目标,盗取一个网上的推导公式:

    假设两个求和符号里面的部分记作L(w, j),那么有

    于是θ的更新公式:

    同理得出x的梯度公式:

    因为x是多个v的累加,word2vec中v的更新方法是:

    想想机器学习真是伟大,整个模型从上到下全是未知数,竟然能算出来我真是服了

    Skip-gram模型

    Skip-gram全称是Continuous Skip-gram Model,是在已知当前词的情况下预测上下文

    Skip-gram模型的神经网络结构设计如下:

    输入层:w的词向量v(w)

    投影层:依然是v(w),就是一个形式

    输出层:和CBOW一样的霍夫曼树

    后面的推导公式和CBOW大同小异,其中θ和v(w)的更新公式除了把符号名从x改成了v(w)之外完全一样,如下:

    体验真实的word2vec

    首先我们从网上下载一个源码,因为google官方的svn库已经不在了,所以只能从csdn下了,但是因为还要花积分才能下载,所以我干脆分享到了我的git上(https://github.com/warmheartli/ChatBotCourse/tree/master/word2vec),大家可以直接下载

    下载下来后直接执行make编译(如果是mac系统要把代码里所有的#include替换成#include)

    编译后生成word2vec、word2phrase、word-analogy、distance、compute-accuracy几个二进制文件

    我们先用word2vec来训练

    首先我们要有训练语料,其实就是已经切好词(空格分隔)的文本,比如我们已经有了这个文本文件叫做train.txt,内容是”人工 智能 一直 以来 是 人类 的 梦想 造 一台 可以 为 你 做 一切 事情 并且 有 情感 的 机器 人”并且重复100遍

    执行

    1
    ./word2vec -train train.txt -output vectors.bin -cbow 0 -size 200 -window 5 -negative 0 -hs 1 -sample 1e-3 -thread 12 -binary 1

    会生成一个vectors.bin文件,这个就是训练好的词向量的二进制文件,利用这个文件我们可以求近义词了,执行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    ./distance vectors.bin
    Enter word or sentence (EXIT to break): 人类
    Word: 人类 Position in vocabulary: 6
    Word Cosine distance
    ------------------------------------------------------------------------
    可以 0.094685
    为 0.091899
    人工 0.088387
    机器 0.076216
    智能 0.073093
    情感 0.071088
    做 0.059367
    一直 0.056979
    以来 0.049426
    一切 0.042201
    </s> 0.025968
    事情 0.014169
    的 0.003633
    是 -0.012021
    有 -0.014790
    一台 -0.021398
    造 -0.031242
    人 -0.043759
    你 -0.072834
    梦想 -0.086062
    并且 -0.122795
    ……

    如果你有很丰富的语料,那么结果会很漂亮

    图解递归神经网络(RNN)

    聊天机器人是需要智能的,而如果他记不住任何信息,就谈不上智能,递归神经网络是一种可以存储记忆的神经网络,LSTM是递归神经网络的一种,在NLP领域应用效果不错,本节我们来介绍RNN和LSTM

    递归神经网络

    递归神经网络(RNN)是两种人工神经网络的总称。一种是时间递归神经网络(recurrent neural network),另一种是结构递归神经网络(recursive neural network)。时间递归神经网络的神经元间连接构成有向图,而结构递归神经网络利用相似的神经网络结构递归构造更为复杂的深度网络。两者训练的算法不同,但属于同一算法变体(百度百科)。本节我们重点介绍时间递归神经网络,下面提到RNN特指时间递归神经网络。

    时间递归神经网络

    传统的神经网络叫做FNN(Feed-Forward Neural Networks),也就是前向反馈神经网络,有关传统神经网络的介绍请见《机器学习教程 十二-神经网络模型的原理》,RNN是在此基础上引入了定向循环,也就是已神经元为节点组成的图中存在有向的环,这种神经网络可以表达某些前后关联关系,事实上,真正的生物神经元之间也是存在这种环形信息传播的,RNN也是神经网络向真实生物神经网络靠近的一个进步。一个典型的RNN是这样的:

    图中隐藏层中的节点之间构成了全连接,也就是一个隐藏层节点的输出可以作为另一个隐藏层节点甚至它自己的输入

    这种结构可以抽象成:

    其中U、V、W都是变换概率矩阵,x是输入,o是输出

    比较容易看出RNN的关键是隐藏层,因为隐藏层能够捕捉到序列的信息,也就是一种记忆的能力

    在RNN中U、V、W的参数都是共享的,也就是只需要关注每一步都在做相同的事情,只是输入不同,这样来降低参数个数和计算量

    RNN在NLP中的应用比较多,因为语言模型就是在已知已经出现的词的情况下预测下一个词的概率的,这正是一个有时序的模型,下一个词的出现取决于前几个词,刚好对应着RNN中隐藏层之间的内部连接

    RNN的训练方法

    RNN的训练方法和传统神经网络一样,都是使用BP误差反向传播算法来更新和训练参数。

    因为从输入到最终的输出中间经过了几步是不确定的,因此为了计算方便,我们利用时序的方式来做前向计算,我们假设x表示输入值,s表示输入x经过U矩阵变换后的值,h表示隐藏层的激活值,o表示输出层的值, f表示隐藏层的激活函数,g表示输出层的激活函数:

    当t=0时,输入为x0, 隐藏层为h0

    当t=1时,输入为x1, s1 = Ux1+Wh0, h1 = f(s1), o1 = g(Vh1)

    当t=2时,s2 = Ux2+Wh1, h2 = f(s2), o2 = g(Vh2)

    以此类推,st = Uxt + Wh(t-1), ht = f(st), ot = g(Vht)

    这里面h=f(现有的输入+过去记忆总结)是对RNN的记忆能力的全然体现

    通过这样的前向推导,我们是不是可以对RNN的结构做一个展开,成如下的样子:

    这样从时序上来看更直观明了

    下面就是反向修正参数的过程了,每一步输出o和实际的o值总会有误差,和传统神经网络反向更新的方法一样,用误差来反向推导,利用链式求导求出每层的梯度,从而更新参数,反向推导过程中我们还是把神经网络结构看成展开后的样子:

    根据链式求导法则,得出隐藏层的残差计算公式为:

    因此W和U的梯度就是:

    LSTM(Long Short Tem Momery networks)

    特别讲解一下LSTM是因为LSTM是一种特别的RNN,它是RNN能得到成功应用的关键,当下非常流行。RNN存在一个长序列依赖(Long-Term Dependencies)的问题:下一个词的出现概率和非常久远的之前的词有关,但考虑到计算量的问题,我们会对依赖的长度做限制,LSTM很好的解决了这个问题,因为它专门为此而设计。

    借用http://colah.github.io/posts/2015-08-Understanding-LSTMs/中经典的几张图来说明下,第一张图是传统RNN的另一种形式的示意图,它只包含一个隐藏层,以tanh为激发函数,这里面的“记忆”体现在t的滑动窗口上,也就是有多少个t就有多少记忆,如下图

    那么我们看LSTM的设计,如下,这里面有一些符号,其中黄色方框是神经网络层(意味着有权重系数和激活函数,σ表示sigmoid激活函数,tanh表示tanh激活函数),粉红圆圈表示矩阵运算(矩阵乘或矩阵加)

    这里需要分部分来说,下面这部分是一个历史信息的传递和记忆,其中粉红×是就像一个能调大小的阀门(乘以一个0到1之间的系数),下面的第一个sigmoid层计算输出0到1之间的系数,作用到粉红×门上,这个操作表达上一阶段传递过来的记忆保留多少,忘掉多少

    其中的sigmoid公式如下:

    可以看出忘掉记忆多少取决于上一隐藏层的输出h{t-1}和本层的输入x{t}

    下面这部分是由上一层的输出h{t-1}和本层的输入x{t}得出的新信息,存到记忆中:

    其中包括计算输出值Ct部分的tanh神经元和计算比例系数的sigmoid神经元(这里面既存在sigmoid又存在tanh原因在于sigmoid取值范围是[0,1]天然作为比例系数,而tanh取值范围是[-1,1]可以作为一个输出值)。其中i{t}和Ct计算公式如下:

    那么Ct输出就是:

    下面部分是隐藏层输出h的计算部分,它考虑了当前拥有的全部信息(上一时序隐藏层的输出、本层的输入x和当前整体的记忆信息),其中本单元状态部分C通过tanh激活并做一个过滤(上一时序输出值和当前输入值通过sigmoid激活后的系数)

    计算公式如下:

    LSTM非常适合在NLP领域应用,比如一句话出现的词可以认为是不同时序的输入x,而在某一时间t出现词A的概率可以通过LSTM计算,因为词A出现的概率是取决于前面出现过的词的,但取决于前面多少个词是不确定的,这正是LSTM所做的存储着记忆信息C,使得能够得出较接近的概率。

    总结

    RNN就是这样一种神经网络,它让隐藏层自身之间存在有向环,从而更接近生物神经网络,也具有了存储记忆的能力,而LSTM作为RNN中更有实用价值的一种,通过它特殊的结构设计实现了永久记忆留存,更适合于NLP,这也为将深度学习应用到自然语言处理开了先河,有记忆是给聊天机器人赋予智能的前提,这也为我们的聊天机器人奠定了实践基础。

    用深度学习来做自动问答的一般方法

    聊天机器人本质上是一个范问答系统,既然是问答系统就离不开候选答案的选择,利用深度学习的方法可以帮助我们找到最佳的答案,本节我们来讲述一下用深度学习来做自动问答的一般方法

    语料库的获取方法

    对于一个范问答系统,一般我们从互联网上收集语料信息,比如百度、谷歌等,用这些结果构建问答对组成的语料库。然后把这些语料库分成多个部分:训练集、开发集、测试集

    问答系统训练其实是训练一个怎么在一堆答案里找到一个正确答案的模型,那么为了让样本更有效,在训练过程中我们不把所有答案都放到一个向量空间中,而是对他们做个分组,首先,我们在语料库里采集样本,收集每一个问题对应的500个答案集合,其中这500个里面有正向的样本,也会随机选一些负向样本放里面,这样就能突出这个正向样本的作用了

    基于CNN的系统设计

    CNN的三个优点:sparse interaction(稀疏的交互),parameter sharing(参数共享),equivalent respresentation(等价表示)。正是由于这三方面的优点,才更适合于自动问答系统中的答案选择模型的训练。

    我们设计卷积公式表示如下(不了解卷积的含义请见《机器学习教程 十五-细解卷积神经网络》):

    假设每个词用三维向量表示,左边是4个词,右边是卷积矩阵,那么得到输出为:

    如果基于这个结果做1-MaxPool池化,那么就取o中的最大值

    通用的训练方法

    训练时获取问题的词向量Vq(这里面词向量可以使用google的word2vec来训练,有关word2vec的内容可以看《自己动手做聊天机器人 二十五-google的文本挖掘深度学习工具word2vec的实现原理》),和一个正向答案的词向量Va+,和一个负向答案的词向量Va-, 然后比较问题和这两个答案的相似度,两个相似度的差值如果大于一个阈值m就用来更新模型参数,然后继续在候选池里选答案,小于m就不更新模型,即优化函数为:

    参数更新方式和其他卷积神经网络方式相同,都是梯度下降、链式求导

    对于测试数据,计算问题和候选答案的cos距离,相似度最大的那个就是正确答案的预测

    神经网络结构设计

    以下是六种结构设计,解释一下,其中HL表示hide layer隐藏层,它的激活函数设计成z = tanh(Wx+B),CNN是卷积层,P是池化层,池化步长为1,T是tanh层,P+T的输出是向量表示,最终的输出是两个向量的cos相似度

    图中HL或CNN连起来的表示他们共享相同的权重。CNN的输出是几维的取决于做多少个卷积特征,如果有4个卷积,那么结果就是4*3的矩阵(这里面的3在下一步被池化后就变成1维了)

    以上结构的效果在论文《Applying Deep Learning To Answer Selection- A Study And An Open Task》中有详细说明,这里不赘述

    总结

    要把深度学习运用到聊天机器人中,关键在于以下几点:

    1. 对几种神经网络结构的选择、组合、优化

    2. 因为是有关自然语言处理,所以少不了能让机器识别的词向量

    3. 当涉及到相似或匹配关系时要考虑相似度计算,典型的方法是cos距离

    4. 如果需求涉及到文本序列的全局信息就用CNN或LSTM

    5. 当精度不高时可以加层

    6. 当计算量过大时别忘了参数共享和池化

    脑洞大开:基于美剧字幕的聊天语料库建设方案

    要让聊天机器人进行学习,需要海量的聊天语料库,但是网上的语料库基本上都是有各种标注的文章,并没有可用的对话语料,虽然有一些社区的帖子数据,但是既要花大把银子还不知道质量如何。笔者突然灵机一动,找到一个妙招能获取海量高质聊天语料,这下聊天机器人再也不愁语料数据了。

    美剧字幕

    是的,你没有看错,我就是这样获取海量高质聊天语料的。外文电影或电视剧的字幕文件是一个天然的聊天语料,尤其是对话比较多的美剧最佳。为了能下载大量美剧字幕,我打算抓取字幕库网站www.zimuku.net,当然你也可以选择其他网站抓取。

    自动抓取字幕

    有关爬虫相关内容请见我的另一篇文章《教你成为全栈工程师(Full Stack Developer) 三十-十分钟掌握最强大的python爬虫》。在这里我直接贴上我的抓取器重要代码(代码共享在了https://github.com/warmheartli/ChatBotCourse):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    # coding:utf-8
    import sys
    reload(sys)
    sys.setdefaultencoding( "utf-8" )
    import scrapy
    from w3lib.html import remove_tags
    from subtitle_crawler.items import SubtitleCrawlerItem
    class SubTitleSpider(scrapy.Spider):
    name = "subtitle"
    allowed_domains = ["zimuku.net"]
    start_urls = [
    "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=20",
    "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=21",
    "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=22",
    ]
    def parse(self, response):
    hrefs = response.selector.xpath('//div[contains(@class, "persub")]/h1/a/@href').extract()
    for href in hrefs:
    url = response.urljoin(href)
    request = scrapy.Request(url, callback=self.parse_detail)
    yield request
    def parse_detail(self, response):
    url = response.selector.xpath('//li[contains(@class, "dlsub")]/div/a/@href').extract()[0]
    print "processing: ", url
    request = scrapy.Request(url, callback=self.parse_file)
    yield request
    def parse_file(self, response):
    body = response.body
    item = SubtitleCrawlerItem()
    item['url'] = response.url
    item['body'] = body
    return item

    下面是pipeline.py代码:

    1
    2
    3
    4
    5
    6
    7
    8
    class SubtitleCrawlerPipeline(object):
    def process_item(self, item, spider):
    url = item['url']
    file_name = url.replace('/','_').replace(':','_')
    fp = open('result/'+file_name, 'w')
    fp.write(item['body'])
    fp.close()
    return item

    看下我抓取的最终效果

    1
    2
    3
    4
    5
    6
    [root@centos:~/Developer/ChatBotCourse/subtitle $] ls result/|head -1
    http___shooter.zimuku.net_download_265300_Hick.2011.720p.BluRay.x264.YIFY.rar
    [root@centos:~/Developer/ChatBotCourse/subtitle $] ls result/|wc -l
    82575
    [root@centos:~/Developer/ChatBotCourse/subtitle $] du -hs result/
    16G result/

    字幕文件的解压方法

    linux下怎么解压zip文件

    直接执行unzip file.zip即可

    linux下怎么解压rar文件

    http://www.rarlab.com/download.htm

    wgethttp://www.rarlab.com/rar/rarlinux-x64-5.4.0.tar.gz

    tar zxvf rarlinux-x64-5.4.0.tar.gz

    ./rar/unrar试试

    解压命令:

    unrar x file.rar

    linux下怎么解压7z文件

    http://downloads.sourceforge.net/project/p7zip下载源文件,解压后执行make编译后bin/7za可用,用法

    bin/7za x file.7z

    最终字幕的处理方式

    有关解压出来的文本字幕文件的处理,我后面的文章会详细讲解如何分词、如何组合,敬请期待。

    重磅:近1GB的三千万聊天语料供出

    经过半个月的倾力打造,建设好的聊天语料库包含三千多万条简体中文高质量聊天语料,近1G的纯文本数据。此语料库全部基于2万部影视剧字幕,经由爬取、分类、解压、语言识别、编码识别、编码转换、过滤清洗等一系列繁琐过程。把整个建设过程分享出来供大家玩耍。

    注意:本文提到的程序和脚本都分享在https://github.com/warmheartli/ChatBotCourse。如需直接获取最终语料库,请见文章末尾。

    第一步:爬取影视剧字幕

    请见我的这篇文章《二十八-脑洞大开:基于美剧字幕的聊天语料库建设方案》

    第二步:压缩格式分类

    下载的字幕有zip格式和rar格式,因为数量比较多,需要做筛选分类,以便后面的处理,这步看似简单实则不易,因为要解决:文件多无法ls的问题、文件名带特殊字符的问题、文件名重名误覆盖问题、扩展名千奇百怪的问题,我写成了python脚本mv_zip.py如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import glob
    import os
    import fnmatch
    import shutil
    import sys
    def iterfindfiles(path, fnexp):
    for root, dirs, files in os.walk(path):
    for filename in fnmatch.filter(files, fnexp):
    yield os.path.join(root, filename)
    i=0
    for filename in iterfindfiles(r"./input/", "*.zip"):
    i=i+1
    newfilename = "zip/" + str(i) + "_" + os.path.basename(filename)
    print filename + " <===> " + newfilename
    shutil.move(filename, newfilename)

    其中的扩展名根据压缩文件可能有的扩展名修改成*.rar*.RAR*.zip*.ZIP

    第三步:解压

    解压这一步需要根据所用的操作系统下载不同的解压工具,建议使用unrar和unzip,为了解决解压后文件名可能重名覆盖的问题,我总结出如下两句脚本来实现批量解压:

    1
    2
    i=0; for file in `ls`; do mkdir output/${i}; echo "unzip $file -d output/${i}";unzip -P abc $file -d output/${i} > /dev/null; ((i++)); done
    i=0; for file in `ls`; do mkdir output/${i}; echo "${i} unrar x $file output/${i}";unrar x $file output/${i} > /dev/null; ((i++)); done

    第四步:srt、ass、ssa字幕文件分类整理

    当你下载大量字幕并解压后你会发现字幕文件类型有很多种,包括srt、lrc、ass、ssa、sup、idx、str、vtt,但是整体量级上来看srt、ass、ssa占绝对优势,因此简单起见,我们抛弃掉其他格式,只保留这三种,具体分类整理的脚本可以参考第二部压缩格式分类的方法按扩展名整理

    第五步:清理目录

    在我边整理边分析的过程中发现,我为了避免重名把文件放到不同目录里后,如果再经过一步文件类型整理,会产生非常多的空目录,每次ls都要拉好几屏,所以写了一个自动清理空目录的脚本clear_empty_dir.py,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import glob
    import os
    import fnmatch
    import shutil
    import sys
    def iterfindfiles(path, fnexp):
    for root, dirs, files in os.walk(path):
    if 0 == len(files) and len(dirs) == 0:
    print root
    os.rmdir(root)
    iterfindfiles(r"./input/", "")

    第六步:清理非字幕文件

    在整个字幕文件分析过程中,总有很多其他文件干扰你的视线,比如txt、html、doc、docx,因为不是我们想要的,因此干脆直接干掉,批量删除脚本del_file.py如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import glob
    import os
    import fnmatch
    import shutil
    import sys
    def iterfindfiles(path, fnexp):
    for root, dirs, files in os.walk(path):
    for filename in fnmatch.filter(files, fnexp):
    yield os.path.join(root, filename)
    for suffix in ("*.mp4", "*.txt", "*.JPG", "*.htm", "*.doc", "*.docx", "*.nfo", "*.sub", "*.idx"):
    for filename in iterfindfiles(r"./input/", suffix):
    print filename
    os.remove(filename)

    第七步:多层解压缩

    把抓取到的字幕压缩包解压后有的文件里面依然还有压缩包,继续解压才能看到字幕文件,因此上面这些步骤再来一次,不过要做好心理准备,没准需要再来n次!

    第八步:舍弃剩余的少量文件

    经过以上几步的处理后剩下一批无扩展名的、特殊扩展名如:“srt.简体”,7z等、少量压缩文件,总体不超过50M,想想伟大思想家马克思教导我们要抓主要矛盾,因此这部分我们直接抛弃掉

    第九步:编码识别与转码

    字幕文件就是这样的没有规范,乃至于各种编码齐聚,什么utf-8、utf-16、gbk、unicode、iso8859琳琅满目应有尽有,我们要统一到一种编码方便使用,索性我们统一到utf-8,get_charset_and_conv.py如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import chardet
    import sys
    import os
    if __name__ == '__main__':
    if len(sys.argv) == 2:
    for root, dirs, files in os.walk(sys.argv[1]):
    for file in files:
    file_path = root + "/" + file
    f = open(file_path,'r')
    data = f.read()
    f.close()
    encoding = chardet.detect(data)["encoding"]
    if encoding not in ("UTF-8-SIG", "UTF-16LE", "utf-8", "ascii"):
    try:
    gb_content = data.decode("gb18030")
    gb_content.encode('utf-8')
    f = open(file_path, 'w')
    f.write(gb_content.encode('utf-8'))
    f.close()
    except:
    print "except:", file_path

    第十步:筛选中文

    考虑到我朝广大人民的爱国热情,我只做中文,所以什么英文、韩文、日文、俄文、火星文、鸟语……全都不要,参考extract_sentence_srt.py如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # coding:utf-8
    import chardet
    import os
    import re
    cn=ur"([\u4e00-\u9fa5]+)"
    pattern_cn = re.compile(cn)
    jp1=ur"([\u3040-\u309F]+)"
    pattern_jp1 = re.compile(jp1)
    jp2=ur"([\u30A0-\u30FF]+)"
    pattern_jp2 = re.compile(jp2)
    for root, dirs, files in os.walk("./srt"):
    file_count = len(files)
    if file_count > 0:
    for index, file in enumerate(files):
    f = open(root + "/" + file, "r")
    content = f.read()
    f.close()
    encoding = chardet.detect(content)["encoding"]
    try:
    for sentence in content.decode(encoding).split('\n'):
    if len(sentence) > 0:
    match_cn = pattern_cn.findall(sentence)
    match_jp1 = pattern_jp1.findall(sentence)
    match_jp2 = pattern_jp2.findall(sentence)
    sentence = sentence.strip()
    if len(match_cn)>0 and len(match_jp1)==0 and len(match_jp2) == 0 and len(sentence)>1 and len(sentence.split(' ')) < 10:
    print sentence.encode('utf-8')
    except:
    continue

    第十一步:字幕中的句子提取

    不同格式的字幕有特定的格式,除了句子之外还有很多字幕的控制语句,我们一律过滤掉,只提取我们想要的重点内容,因为不同的格式都不一样,在这里不一一举例了,感兴趣可以去我的github查看,在这里单独列出ssa格式字幕的部分代码供参考:

    1
    2
    3
    4
    5
    6
    if line.find('Dialogue') == 0 and len(line) < 500:
    fields = line.split(',')
    sentence = fields[len(fields)-1]
    tag_fields = sentence.split('}')
    if len(tag_fields) > 1:
    sentence = tag_fields[len(tag_fields)-1]

    第十二步:内容过滤

    经过上面几步的处理,其实已经形成了完整的语料库了,只是里面还有一些不像聊天的内容我们需要进一步做优化,包括:过滤特殊的unicode字符、过滤特殊的关键词(如:字幕、时间轴、校对……)、去除字幕样式标签、去除html标签、去除连续特殊字符、去除转义字符、去除剧集信息等,具体代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    # coding:utf-8
    import sys
    import re
    import chardet
    if __name__ == '__main__':
    #illegal=ur"([\u2000-\u2010]+)"
    illegal=ur"([\u0000-\u2010]+)"
    pattern_illegals = [re.compile(ur"([\u2000-\u2010]+)"), re.compile(ur"([\u0090-\u0099]+)")]
    filters = ["字幕", "时间轴:", "校对:", "翻译:", "后期:", "监制:"]
    filters.append("时间轴:")
    filters.append("校对:")
    filters.append("翻译:")
    filters.append("后期:")
    filters.append("监制:")
    filters.append("禁止用作任何商业盈利行为")
    filters.append("http")
    htmltagregex = re.compile(r'<[^>]+>',re.S)
    brace_regex = re.compile(r'\{.*\}',re.S)
    slash_regex = re.compile(r'\\\w',re.S)
    repeat_regex = re.compile(r'[-=]{10}',re.S)
    f = open("./corpus/all.out", "r")
    count=0
    while True:
    line = f.readline()
    if line:
    line = line.strip()
    # 编码识别,不是utf-8就过滤
    gb_content = ''
    try:
    gb_content = line.decode("utf-8")
    except Exception as e:
    sys.stderr.write("decode error: ", line)
    continue
    # 中文识别,不是中文就过滤
    need_continue = False
    for pattern_illegal in pattern_illegals:
    match_illegal = pattern_illegal.findall(gb_content)
    if len(match_illegal) > 0:
    sys.stderr.write("match_illegal error: %s\n" % line)
    need_continue = True
    break
    if need_continue:
    continue
    # 关键词过滤
    need_continue = False
    for filter in filters:
    try:
    line.index(filter)
    sys.stderr.write("filter keyword of %s %s\n" % (filter, line))
    need_continue = True
    break
    except:
    pass
    if need_continue:
    continue
    # 去掉剧集信息
    if re.match('.*第.*季.*', line):
    sys.stderr.write("filter copora %s\n" % line)
    continue
    if re.match('.*第.*集.*', line):
    sys.stderr.write("filter copora %s\n" % line)
    continue
    if re.match('.*第.*帧.*', line):
    sys.stderr.write("filter copora %s\n" % line)
    continue
    # 去html标签
    line = htmltagregex.sub('',line)
    # 去花括号修饰
    line = brace_regex.sub('', line)
    # 去转义
    line = slash_regex.sub('', line)
    # 去重复
    new_line = repeat_regex.sub('', line)
    if len(new_line) != len(line):
    continue
    # 去特殊字符
    line = line.replace('-', '').strip()
    if len(line) > 0:
    sys.stdout.write("%s\n" % line)
    count+=1
    else:
    break
    f.close()
    pass

    数据样例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    这是什么
    是寄给医院的
    井崎…为什么?
    是为了小雪的事情
    怎么回事?
    您不记得了吗
    在她说小雪…
    就是在这种非常时期和我们一起舍弃休息时间来工作的护士失踪时…
    医生 小雪她失踪了
    你不是回了一句「是吗」吗
    是吗…
    不 对不起
    跟我道歉也没用啊
    而且我们都知道您是因为夫人的事情而操劳
    但是 我想小聪是受不了医生一副漠不关心的样子
    事到如今再责备医生也没有用了
    是我的错吗…
    我就是这个意思 您听不出来吗
    我也难以接受
    ……

    第一版聊天机器人诞生——吃了字幕长大的小二兔

    在上一节中我分享了建设好的影视剧字幕聊天语料库,本节基于这个语料库开发我们的聊天机器人,因为是第一版,所以机器人的思绪还有点小乱,答非所问、驴唇不对马嘴得比较搞笑,大家凑合玩

    第一版思路

    首先要考虑到我的影视剧字幕聊天语料库特点,它是把影视剧里面的说话内容一句一句以回车换行罗列的三千多万条中国话,那么相邻的第二句其实就很可能是第一句的最好的回答,另外,如果对于一个问句有很多种回答,那么我们可以根据相关程度以及历史聊天记录来把所有回答排个序,找到最优的那个,这么说来这是一个搜索和排序的过程。对!没错!我们可以借助搜索技术来做第一版。

    lucene+ik

    lucene是一款开源免费的搜索引擎库,java语言开发。ik全称是IKAnalyzer,是一个开源中文切词工具。我们可以利用这两个工具来对语料库做切词建索引,并通过文本搜索的方式做文本相关性检索,然后把下一句取出来作为答案候选集,然后再通过各种方式做答案排序,当然这个排序是很有学问的,聊天机器人有没有智能一半程度上体现在了这里(还有一半体现在对问题的分析上),本节我们的主要目的是打通这一套机制,至于“智能”这件事我们以后逐个拆解开来不断研究

    建索引

    首先用eclipse创建一个maven工程,如下:

    maven帮我们自动生成了pom.xml文件,这配置了包依赖信息,我们在dependencies标签中添加如下依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>4.10.4</version>
    </dependency>
    <dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-queryparser</artifactId>
    <version>4.10.4</version>
    </dependency>
    <dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-common</artifactId>
    <version>4.10.4</version>
    </dependency>
    <dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>5.0.0.Alpha2</version>
    </dependency>
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.1.41</version>
    </dependency>

    并在project标签中增加如下配置,使得依赖的jar包都能自动拷贝到lib目录下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
    <execution>
    <id>copy-dependencies</id>
    <phase>prepare-package</phase>
    <goals>
    <goal>copy-dependencies</goal>
    </goals>
    <configuration>
    <outputDirectory>${project.build.directory}/lib</outputDirectory>
    <overWriteReleases>false</overWriteReleases>
    <overWriteSnapshots>false</overWriteSnapshots>
    <overWriteIfNewer>true</overWriteIfNewer>
    </configuration>
    </execution>
    </executions>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
    <archive>
    <manifest>
    <addClasspath>true</addClasspath>
    <classpathPrefix>lib/</classpathPrefix>
    <mainClass>theMainClass</mainClass>
    </manifest>
    </archive>
    </configuration>
    </plugin>
    </plugins>
    </build>

    https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/ik-analyzer/IK%20Analyzer%202012FF_hf1_source.rar下载ik的源代码并把其中的src/org目录拷贝到chatbotv1工程的src/main/java下,然后刷新maven工程,效果如下:

    在com.shareditor.chatbotv1包下maven帮我们自动生成了App.java,为了辨识我们改成Indexer.java,关键代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    Analyzer analyzer = new IKAnalyzer(true);
    IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_4_9, analyzer);
    iwc.setOpenMode(OpenMode.CREATE);
    iwc.setUseCompoundFile(true);
    IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File(indexPath)), iwc);
    BufferedReader br = new BufferedReader(new InputStreamReader(
    new FileInputStream(corpusPath), "UTF-8"));
    String line = "";
    String last = "";
    long lineNum = 0;
    while ((line = br.readLine()) != null) {
    line = line.trim();
    if (0 == line.length()) {
    continue;
    }
    if (!last.equals("")) {
    Document doc = new Document();
    doc.add(new TextField("question", last, Store.YES));
    doc.add(new StoredField("answer", line));
    indexWriter.addDocument(doc);
    }
    last = line;
    lineNum++;
    if (lineNum % 100000 == 0) {
    System.out.println("add doc " + lineNum);
    }
    }
    br.close();
    indexWriter.forceMerge(1);
    indexWriter.close();

    编译好后拷贝src/main/resources下的所有文件到target目录下,并在target目录下执行

    1
    java -cp $CLASSPATH:./lib/:./chatbotv1-0.0.1-SNAPSHOT.jar com.shareditor.chatbotv1.Indexer ../../subtitle/raw_subtitles/subtitle.corpus ./index

    最终生成的索引目录index通过lukeall-4.9.0.jar查看如下:

    检索服务

    基于netty创建一个http服务server,代码共享在https://github.com/warmheartli/ChatBotCourse的chatbotv1目录下,关键代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    Analyzer analyzer = new IKAnalyzer(true);
    QueryParser qp = new QueryParser(Version.LUCENE_4_9, "question", analyzer);
    if (topDocs.totalHits == 0) {
    qp.setDefaultOperator(Operator.AND);
    query = qp.parse(q);
    System.out.println(query.toString());
    indexSearcher.search(query, collector);
    topDocs = collector.topDocs();
    }
    if (topDocs.totalHits == 0) {
    qp.setDefaultOperator(Operator.OR);
    query = qp.parse(q);
    System.out.println(query.toString());
    indexSearcher.search(query, collector);
    topDocs = collector.topDocs();
    }
    ret.put("total", topDocs.totalHits);
    ret.put("q", q);
    JSONArray result = new JSONArray();
    for (ScoreDoc d : topDocs.scoreDocs) {
    Document doc = indexSearcher.doc(d.doc);
    String question = doc.get("question");
    String answer = doc.get("answer");
    JSONObject item = new JSONObject();
    item.put("question", question);
    item.put("answer", answer);
    item.put("score", d.score);
    item.put("doc", d.doc);
    result.add(item);
    }
    ret.put("result", result);

    其实就是查询建好的索引,通过query词做切词拼lucene query,然后检索索引的question字段,匹配上的返回answer字段的值作为候选集,使用时挑出候选集里的一条作为答案

    这个server可以通过http访问,如http://127.0.0.1:8765/?q=hello(注意:如果是中文需要转成urlcode发送,因为java端读取时按照urlcode解析),server的启动方法是:

    1
    java -cp $CLASSPATH:./lib/:./chatbotv1-0.0.1-SNAPSHOT.jar com.shareditor.chatbotv1.Searcher

    聊天界面

    先看下我们的界面是什么样的,然后再说怎么做的

    首先需要有一个可以展示聊天内容的框框,我们选择ckeditor,因为它支持html格式内容的展示,然后就是一个输入框和发送按钮,html代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <div class="col-sm-4 col-xs-10">
    <div class="row">
    <textarea id="chatarea">
    <div style='color: blue; text-align: left; padding: 5px;'>机器人: 喂,大哥您好,您终于肯跟我聊天了,来侃侃呗,我来者不拒!</div>
    <div style='color: blue; text-align: left; padding: 5px;'>机器人: 啥?你问我怎么这么聪明会聊天?因为我刚刚吃了一堆影视剧字幕!</div>
    </textarea>
    </div>
    <br />
    <div class="row">
    <div class="input-group">
    <input type="text" id="input" class="form-control" autofocus="autofocus" onkeydown="submitByEnter()" />
    <span class="input-group-btn">
    <button class="btn btn-default" type="button" onclick="submit()">发送</button>
    </span>
    </div>
    </div>
    </div>
    <script type="text/javascript">
    CKEDITOR.replace('chatarea',
    {
    readOnly: true,
    toolbar: ['Source'],
    height: 500,
    removePlugins: 'elementspath',
    resize_enabled: false,
    allowedContent: true
    });
    </script>

    为了调用上面的聊天server,需要实现一个发送请求获取结果的控制器,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public function queryAction(Request $request)
    {
    $q = $request->get('input');
    $opts = array(
    'http'=>array(
    'method'=>"GET",
    'timeout'=>60,
    )
    );
    $context = stream_context_create($opts);
    $clientIp = $request->getClientIp();
    $response = file_get_contents('http://127.0.0.1:8765/?q=' . urlencode($q) . '&clientIp=' . $clientIp, false, $context);
    $res = json_decode($response, true);
    $total = $res['total'];
    $result = '';
    if ($total > 0) {
    $result = $res['result'][0]['answer'];
    }
    return new Response($result);
    }

    这个控制器的路由配置为:

    1
    2
    3
    chatbot_query:
    path: /chatbot/query
    defaults: { _controller: AppBundle:ChatBot:query }

    因为聊天server响应时间比较长,为了不导致web界面卡住,我们在执行submit的时候异步发请求和收结果,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    var xmlHttp;
    function submit() {
    if (window.ActiveXObject) {
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
    xmlHttp = new XMLHttpRequest();
    }
    var input = $("#input").val().trim();
    if (input == '') {
    jQuery('#input').val('');
    return;
    }
    addText(input, false);
    jQuery('#input').val('');
    var datastr = "input=" + input;
    datastr = encodeURI(datastr);
    var url = "/chatbot/query";
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = callback;
    xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xmlHttp.send(datastr);
    }
    function callback() {
    if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
    var responseText = xmlHttp.responseText;
    addText(responseText, true);
    }
    }

    这里的addText是往ckeditor里添加一段文本,方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function addText(text, is_response) {
    var oldText = CKEDITOR.instances.chatarea.getData();
    var prefix = '';
    if (is_response) {
    prefix = "<div style='color: blue; text-align: left; padding: 5px;'>机器人: "
    } else {
    prefix = "<div style='color: darkgreen; text-align: right; padding: 5px;'>我: "
    }
    CKEDITOR.instances.chatarea.setData(oldText + "" + prefix + text + "</div>");
    }

    以上所有代码全都共享在https://github.com/warmheartli/ChatBotCoursehttps://github.com/warmheartli/shareditor.com中供参考


    python 中文文本分类 - CSDN博客

    $
    0
    0

    一,中文文本分类流程:

    1,预处理

    2,中文分词

    3,结构化表示--构建词向量空间

    4,权重策略--TF-IDF

    5,分类器

    6,评价



    二,具体细节

    1,预处理。希望得到这样的目标:

    1.1得到训练集语料库

    即已经分好类的文本资料(例如:语料库里是一系列txt文章,这些文章按照主题归入到不同分类的目录中,如 .\art\21.txt)

    推荐语料库:复旦中文文本分类语料库,下载链接:http://download.csdn.net/detail/github_36326955/9747927

    将下载的语料库解压后,请自己修改文件名和路径,例如路径可以设置为 ./train_corpus/,

    其下则是各个类别目录如:./train_corpus/C3-Art,……,\train_corpus\C39-Sports

    1.2得到测试语料库

    也是已经分好类的文本资料,与1.1类型相同,只是里面的文档不同,用于检测算法的实际效果。测试预料可以从1.1中的训练预料中随机抽取,也可以下载独立的测试语料库,复旦中文文本分类语料库测试集链接:http://download.csdn.net/detail/github_36326955/9747929

    路径修改参考1.1,例如可以设置为 ./test_corpus/

    1.3其他

    你可能希望从自己爬取到的网页等内容中获取新文本,用本节内容进行实际的文本分类,这时候,你可能需要将html标签去除来获取文本格式的文档,这里提供一个基于python 和lxml的样例代码:

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    """
    @version: python2.7.8 
    @author: XiangguoSun
    @contact: sunxiangguodut@qq.com
    @file: html_demo.py
    @time: 2017/2/6 12:25
    @software: PyCharm
    """
    import sys
    from lxml import html
    # 设置utf-8 unicode环境
    reload(sys)
    sys.setdefaultencoding('utf-8')
    def html2txt(path):
        with open(path,"rb") as f:
            content=f.read() 
        r'''上面两行是python2.6以上版本增加的语法,省略了繁琐的文件close和try操作
        2.5版本需要from __future__ import with_statement新手可以参考这个链接来学习http://zhoutall.com/archives/325
        '''
        page = html.document_fromstring(content) # 解析文件
        text = page.text_content() # 去除所有标签
        return text
    if __name__  =="__main__":
        # htm文件路径,以及读取文件
        path = "1.htm"
        text=html2txt(path)
        print text	 # 输出去除标签后解析结果




    2,中文分词。

    本小节的目标是:

    1,告诉你中文分词的实际操作。

    2,告诉你中文分词的算法。





    2.1概述

    第1小节预处理中的语料库都是没有分词的原始语料(即连续的句子,而后面的工作需要我们把文本分为一个个单词),现在需要对这些文本进行分词,只有这样,才能在 基于单词的基础上,对文档进行结构化表示。

    中文分词有其特有的难点(相对于英文而言),最终完全解决中文分词的算法是基于概率图模型的条件随机场(CRF)。(可以参考博主的另一篇博文)

    当然,在实际操作中,即使你对于相关算法不甚了解,也不影响你的操作,中文分词的工具有很多。但是比较著名的几个都是基于java的,这里推荐python的第三方库jieba(所采用的算法就是条件随机场)。对于非专业文档绰绰有余。如果你有强迫症,希望得到更高精度的分词工具,可以使用开源项目Anjs(基于java),你可以将这个开源项目与python整合。

    关于分词库的更多讨论可以参考这篇文章:https://www.zhihu.com/question/19651613



    你可以通过pip安装jieba:打开cmd,切换到目录  .../python/scripts/,执行命令:pip install jieba

    或者你也可以在集成开发平台上安装jieba,例如,如果你用的是pycharm,可以点击file-settings-project:xxx-Projuect Interpreter.其他平台也都有类似的安装方法。



    2.2分词操作

    不要担心下面的代码你看不懂,我会非常详细的进行讲解,确保python入门级别水平的人都可以看懂:

    2.2.1

    首先讲解jieba分词使用方法(详细的和更进一步的,可以参考这个链接):

    jieba.cut 方法接受三个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式;HMM 参数用来控制是否使用 HMM 模型
    jieba.cut_for_search 方法接受两个参数:需要分词的字符串;是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细
    待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8
    jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用
    jieba.lcut 以及 jieba.lcut_for_search 直接返回 list
    jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定义分词器,可用于同时使用不同词典。jieba.dt 为默认分词器,所有全局分词相关函数都是该分词器的映射。
    实例代码:

    import jieba
    seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
    print("Full Mode: " + "/ ".join(seg_list))  # 全模式
    seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
    print("Default Mode: " + "/ ".join(seg_list))  # 精确模式
    seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式
    print(", ".join(seg_list))
    seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
    print(", ".join(seg_list))


    输出:
    【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学
    【精确模式】: 我/ 来到/ 北京/ 清华大学
    【新词识别】:他, 来到, 了, 网易, 杭研, 大厦    (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)
    【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大



    2.2.2

    接下来,我们将要通过python编程,来将1.1节中的 ./train_corpus/原始训练语料库和1.2节中的./test_corpus/原始测试语料库进行分词,分词后保存的路径可以设置为

    ./train_corpus_seg/和./test_corpus_seg/

    代码如下,思路很简单,就是遍历所有的txt文本,然后将每个文本依次进行分词。你唯一需要注意的就是写好自己的路径,不要出错。下面的代码已经给出了非常详尽的解释,初学者也可以看懂。如果你还没有明白,或者在运行中出现问题(其实根本不可能出现问题,我写的代码,质量很高的。。。),都可以发邮件给我,邮件地址在代码中,或者在博文下方评论中给出。



    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    """
    @version: python2.7.8 
    @author: XiangguoSun
    @contact: sunxiangguodut@qq.com
    @file: corpus_segment.py
    @time: 2017/2/5 15:28
    @software: PyCharm
    """
    import sys
    import os
    import jieba
    # 配置utf-8输出环境
    reload(sys)
    sys.setdefaultencoding('utf-8')
    # 保存至文件
    def savefile(savepath, content):
        with open(savepath, "wb") as fp:
            fp.write(content)
        '''上面两行是python2.6以上版本增加的语法,省略了繁琐的文件close和try操作
        2.5版本需要from __future__ import with_statement新手可以参考这个链接来学习http://zhoutall.com/archives/325
        '''
    # 读取文件
    def readfile(path):
        with open(path, "rb") as fp:
            content = fp.read()
        return content
    def corpus_segment(corpus_path, seg_path):
        '''
        corpus_path是未分词语料库路径
        seg_path是分词后语料库存储路径
        '''
        catelist = os.listdir(corpus_path)  # 获取corpus_path下的所有子目录
        '''其中子目录的名字就是类别名,例如:
        train_corpus/art/21.txt中,'train_corpus/'是corpus_path,'art'是catelist中的一个成员
        '''
        # 获取每个目录(类别)下所有的文件
        for mydir in catelist:
            '''这里mydir就是train_corpus/art/21.txt中的art(即catelist中的一个类别)
            '''
            class_path = corpus_path + mydir + "/"  # 拼出分类子目录的路径如:train_corpus/art/
            seg_dir = seg_path + mydir + "/"  # 拼出分词后存贮的对应目录路径如:train_corpus_seg/art/
            if not os.path.exists(seg_dir):  # 是否存在分词目录,如果没有则创建该目录
                os.makedirs(seg_dir)
            file_list = os.listdir(class_path)  # 获取未分词语料库中某一类别中的所有文本
            '''
            train_corpus/art/中的
            21.txt,
            22.txt,
            23.txt
            ...
            file_list=['21.txt','22.txt',...]
            '''
            for file_path in file_list:  # 遍历类别目录下的所有文件
                fullname = class_path + file_path  # 拼出文件名全路径如:train_corpus/art/21.txt
                content = readfile(fullname)  # 读取文件内容
                '''此时,content里面存贮的是原文本的所有字符,例如多余的空格、空行、回车等等,接下来,我们需要把这些无关痛痒的字符统统去掉,变成只有标点符号做间隔的紧凑的文本内容
                '''
                content = content.replace("\r\n", "")  # 删除换行
                content = content.replace(" ", "")#删除空行、多余的空格
                content_seg = jieba.cut(content)  # 为文件内容分词
                savefile(seg_dir + file_path, " ".join(content_seg))  # 将处理后的文件保存到分词后语料目录
        print "中文语料分词结束!!!"
    '''
    如果你对if __name__=="__main__":这句不懂,可以参考下面的文章
    http://imoyao.lofter.com/post/3492bc_bd0c4ce
    简单来说如果其他python文件调用这个文件的函数,或者把这个文件作为模块
    导入到你的工程中时,那么下面的代码将不会被执行,而如果单独在命令行中
    运行这个文件,或者在IDE(如pycharm)中运行这个文件时候,下面的代码才会运行。
    即,这部分代码相当于一个功能测试。
    如果你还没懂,建议你放弃IT这个行业。
    '''
    if __name__=="__main__":
        #对训练集进行分词
        corpus_path = "./train_corpus/"  # 未分词分类语料库路径
        seg_path = "./train_corpus_seg/"  # 分词后分类语料库路径
        corpus_segment(corpus_path,seg_path)
        #对测试集进行分词
        corpus_path = "./test_corpus/"  # 未分词分类语料库路径
        seg_path = "./test_corpus_seg/"  # 分词后分类语料库路径
        corpus_segment(corpus_path,seg_path)




    截止目前,我们已经得到了分词后的训练集语料库和测试集语料库,下面我们要把这两个数据集表示为变量,从而为下面程序调用提供服务。我们采用的是Scikit-Learn库中的Bunch数据结构来表示这两个数据集。你或许对于Scikit-Learn和Bunch并不是特别了解,而官方的技术文档有两千多页你可能也没耐心去看,好在你们有相国大人。下面我们 以这两个数据集为背景,对Bunch做一个非常通俗的讲解,肯定会让你一下子就明白。

    首先来看看Bunch:

    Bunch这玩意儿,其实就相当于python中的字典。你往里面传什么,它就存什么。

    好了,解释完了。

    是不是很简单?

    在本篇博文中,你对Bunch能够有这种层次的理解,就足够了。如果你想继续详细透彻的理解Bunch,请见博主的另一篇博文《暂时还没写,写完在这里更新链接》



    接下来,让我们看看的我们的数据集(训练集)有哪些信息:

    1,类别,也就是所有分类类别的集合,即我们./train_corpus_seg/和./test_corpus_seg/下的所有子目录的名字。我们在这里不妨把它叫做target_name(这是一个列表)

    2,文本文件名。例如./train_corpus_seg/art/21.txt,我们可以把所有文件名集合在一起做一个列表,叫做filenames

    3,文本标签(就是文本的类别),不妨叫做label(与2中的filenames一一对应)

    例如2中的文本“21.txt”在./train_corpus_seg/art/目录下,则它的标签就是art。

    文本标签与1中的类别区别在于:文本标签集合里面的元素就是1中类别,而文本标签集合的元素是可以重复的,因为./train_corpus_seg/art/目录下有好多文本,不是吗?相应的,1中的类别集合元素显然都是独一无二的类别。

    4,文本内容(contens)。

    上一步代码我们已经成功的把文本内容进行了分词,并且去除掉了所有的换行,得到的其实就是一行词袋(词向量),每个文本文件都是一个词向量。这里的文本内容指的就是这些词向量。



    那么,用Bunch表示,就是:

    from sklearn.datasets.base import Bunch

    bunch = Bunch(target_name=[],label=[],filenames=[],contents=[]) 



    我们在Bunch对象里面创建了有4个成员:

    target_name:是一个list,存放的是整个数据集的类别集合。

    label:是一个list,存放的是所有文本的标签。

    filenames:是一个list,存放的是所有文本文件的名字。

    contents:是一个list,分词后文本文件词向量形式



    如果你还没有明白,看一下下面这个图,你总该明白了:

    Bunch:



    下面,我们将文本文件转为Bunch类形:

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    """
    @version: python2.7.8 
    @author: XiangguoSun
    @contact: sunxiangguodut@qq.com
    @file: corpus2Bunch.py
    @time: 2017/2/7 7:41
    @software: PyCharm
    """
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    import os#python内置的包,用于进行文件目录操作,我们将会用到os.listdir函数
    import cPickle as pickle#导入cPickle包并且取一个别名pickle
    '''
    事实上python中还有一个也叫作pickle的包,与这里的名字相同了,无所谓
    关于cPickle与pickle,请参考博主另一篇博文:
    python核心模块之pickle和cPickle讲解
    http://blog.csdn.net/github_36326955/article/details/54882506
    本文件代码下面会用到cPickle中的函数cPickle.dump
    '''
    from sklearn.datasets.base import Bunch
    #这个您无需做过多了解,您只需要记住以后导入Bunch数据结构就像这样就可以了。
    #今后的博文会对sklearn做更有针对性的讲解
    def _readfile(path):
        '''读取文件'''
        #函数名前面带一个_,是标识私有函数
        # 仅仅用于标明而已,不起什么作用,
        # 外面想调用还是可以调用,
        # 只是增强了程序的可读性
        with open(path, "rb") as fp:#with as句法前面的代码已经多次介绍过,今后不再注释
            content = fp.read()
        return content
    def corpus2Bunch(wordbag_path,seg_path):
        catelist = os.listdir(seg_path)# 获取seg_path下的所有子目录,也就是分类信息
        #创建一个Bunch实例
        bunch = Bunch(target_name=[], label=[], filenames=[], contents=[])
        bunch.target_name.extend(catelist)
        '''
        extend(addlist)是python list中的函数,意思是用新的list(addlist)去扩充原来的list
        '''
        # 获取每个目录下所有的文件
        for mydir in catelist:
            class_path = seg_path + mydir + "/"  # 拼出分类子目录的路径
            file_list = os.listdir(class_path)  # 获取class_path下的所有文件
            for file_path in file_list:  # 遍历类别目录下文件
                fullname = class_path + file_path  # 拼出文件名全路径
                bunch.label.append(mydir)
                bunch.filenames.append(fullname)
                bunch.contents.append(_readfile(fullname))  # 读取文件内容
                '''append(element)是python list中的函数,意思是向原来的list中添加element,注意与extend()函数的区别'''
        # 将bunch存储到wordbag_path路径中
        with open(wordbag_path, "wb") as file_obj:
            pickle.dump(bunch, file_obj)
        print "构建文本对象结束!!!"
    if __name__ == "__main__":#这个语句前面的代码已经介绍过,今后不再注释
        #对训练集进行Bunch化操作:
        wordbag_path = "train_word_bag/train_set.dat"  # Bunch存储路径
        seg_path = "train_corpus_seg/"  # 分词后分类语料库路径
        corpus2Bunch(wordbag_path, seg_path)
        # 对测试集进行Bunch化操作:
        wordbag_path = "test_word_bag/test_set.dat"  # Bunch存储路径
        seg_path = "test_corpus_seg/"  # 分词后分类语料库路径
        corpus2Bunch(wordbag_path, seg_path)



    3,结构化表示--向量空间模型

    在第2节中,我们对原始数据集进行了分词处理,并且通过绑定为Bunch数据类型,实现了数据集的变量表示。事实上在第2节中,我们通过分词,已经将每一个文本文件表示为了一个词向量了。也许你对于什么是词向量并没有清晰的概念,这里有一篇非常棒的文章《Deep Learning in NLP (一)词向量和语言模型》,简单来讲,词向量就是词向量空间里面的一个向量。

    你可以类比为三维空间里面的一个向量,例如:

    如果我们规定词向量空间为:(我,喜欢,相国大人),这相当于三维空间里面的(x,y,z)只不过这里的x,y,z的名字变成了“我”,“喜欢”,“相国大人”



    现在有一个词向量是:我喜欢  喜欢相国大人

    表示在词向量空间中就变为:(1,2,1),归一化后可以表示为:(0.166666666667 0.333333333333 0.166666666667)表示在刚才的词向量空间中就是这样:





    但是在我们第2节处理的这些文件中,词向量之间的单词个数并不相同,词向量的涵盖的单词也不尽相同。他们并不在一个空间里,换句话说,就是他们之间没有可比性,例如:

    词向量1:我喜欢相国大人,对应的词向量空间是(我,喜欢,相国大人),可以表示为(1,1,1)

    词向量2:她喜欢我,对应的词向量空间是(她,不,喜欢,我),可以表示为(1,1,1,1)

    两个空间不一样



    因此,接下来我们要做的,就是把所有这些词向量统一到同一个词向量空间中,例如,在上面的例子中,我们可以设置词向量空间为(我,喜欢,相国大人,她,不)

    这样,词向量1和词向量2分别可以表示为(1,1,1,0,0)和(1,1,0,1,1),这样两个向量就都在同一个空间里面了。可以进行比较和各种运算了。



    也许你已经发现了,这样做的一个很糟糕的结果是,我们要把训练集内所有出现过的单词,都作为一个维度,构建统一的词向量空间,即使是中等大小的文本集合,向量维度也很轻易就达到数十万维。为了节省空间,我们首先将训练集中每个文本中一些垃圾词汇去掉。所谓的垃圾词汇,就是指意义模糊的词,或者一些语气助词,标点符号等等,通常他们对文本起不了分类特征的意义。这些垃圾词汇我们称之为停用词。把所有停用词集合起来构成一张停用词表格,这样,以后我们处理文本时,就可以从这个根据表格,过滤掉文本中的一些垃圾词汇了。

    你可以从这里下载停用词表:hlt_stop_words.txt

    存放在这里路径中:train_word_bag/hlt_stop_words.txt



    下面的程序,目的就是要将训练集所有文本文件(词向量)统一到同一个词向量空间中。值得一提的是,在词向量空间中,事实上不同的词,它的权重是不同的,它对文本分类的影响力也不同,为此我们希望得到的词向量空间不是等权重的空间,而是不同权重的词向量空间。我们把带有不同权重的词向量空间叫做“加权词向量空间”,也有的技术文档将其称为“加权向量词袋”,一个意思。



    现在的问题是,如何计算不同词的权重呢?



    4,权重策略--TF-IDF

    什么是TF-IDF?今后有精力我会在这里更新补充,现在,先给你推荐一篇非常棒的文章《使用scikit-learn工具计算文本TF-IDF值



    下面,我们假定你已经对TF-IDF有了最基本的了解。请你动动你的小脑袋瓜想一想,我们把训练集文本转换成了一个TF-IDF词向量空间,姑且叫它为A空间吧。那么我们还有测试集数据,我们以后实际运用时,还会有新的数据,这些数据显然也要转到词向量空间,那么应该和A空间为同一个空间吗?

    是的。

    即使测试集出现了新的词汇(不是停用词),即使新的文本数据有新的词汇,只要它不是训练集生成的TF-IDF词向量空间中的词,我们就都不予考虑。这就实现了所有文本词向量空间“大一统”,也只有这样,大家才在同一个世界里。才能进行下一步的研究。



    下面的程序就是要将训练集所有文本文件(词向量)统一到同一个TF-IDF词向量空间中(或者叫做用TF-IDF算法计算权重的有权词向量空间)。这个词向量空间最终存放在train_word_bag/tfdifspace.dat中。

    这段代码你可能有点看不懂,因为我估计你可能比较懒,还没看过TF-IDF(尽管我刚才已经给你推荐那篇文章了)。你只需要明白,它把一大坨训练集数据成功的构建了一个TF-IDF词向量空间,空间的各个词都是出自这个训练集(去掉了停用词)中,各个词的权值也都一并保存了下来,叫做权重矩阵。

    需要注意的是,你要明白,权重矩阵是一个二维矩阵,a[i][j]表示,第i个词在第j个类别中的IF-IDF值(看到这里,我估计你压根就没去看那篇文章,所以你可能到现在也不知道 这是个啥玩意儿。。。)



    请记住权重矩阵这个词,代码解释中我会用到。

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    """
    @version: python2.7.8 
    @author: XiangguoSun
    @contact: sunxiangguodut@qq.com
    @file: vector_space.py
    @time: 2017/2/7 17:29
    @software: PyCharm
    """
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    # 引入Bunch类
    from sklearn.datasets.base import Bunch
    import cPickle as pickle#之前已经说过,不再赘述
    from sklearn.feature_extraction.text import TfidfVectorizer#这个东西下面会讲
    # 读取文件
    def _readfile(path):
        with open(path, "rb") as fp:
            content = fp.read()
        return content
    # 读取bunch对象
    def _readbunchobj(path):
        with open(path, "rb") as file_obj:
            bunch = pickle.load(file_obj)
        return bunch
    # 写入bunch对象
    def _writebunchobj(path, bunchobj):
        with open(path, "wb") as file_obj:
            pickle.dump(bunchobj, file_obj)
    #这个函数用于创建TF-IDF词向量空间
    def vector_space(stopword_path,bunch_path,space_path):
        stpwrdlst = _readfile(stopword_path).splitlines()#读取停用词
        bunch = _readbunchobj(bunch_path)#导入分词后的词向量bunch对象
        #构建tf-idf词向量空间对象
        tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
        '''在前面几节中,我们已经介绍了Bunch。
        target_name,label和filenames这几个成员都是我们自己定义的玩意儿,前面已经讲过不再赘述。下面我们讲一下tdm和vocabulary(这俩玩意儿也都是我们自己创建的):
        tdm存放的是计算后得到的TF-IDF权重矩阵。请记住,我们后面分类器需要的东西,其实就是训练集的tdm和标签label,因此这个成员是很重要的。
        vocabulary是词向量空间的索引,例如,如果我们定义的词向量空间是(我,喜欢,相国大人),那么vocabulary就是这样一个索引字典
        vocabulary={"我":0,"喜欢":1,"相国大人":2},你可以简单的理解为:vocabulary就是词向量空间的坐标轴,索引值相当于表明了第几个维度。我们现在就是要构建一个词向量空间,因此在初始时刻,这个tdm和vocabulary自然都是空的。如果你在这一步将vocabulary赋值了一个自定义的内容,那么,你是傻逼。
        '''
        '''与下面这2行代码等价的代码是:
        vectorizer=CountVectorizer()#构建一个计算词频(TF)的玩意儿,当然这里面不只是可以做这些
        transformer=TfidfTransformer()#构建一个计算TF-IDF的玩意儿
        tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))
        #vectorizer.fit_transform(corpus)将文本corpus输入,得到词频矩阵
        #将这个矩阵作为输入,用transformer.fit_transform(词频矩阵)得到TF-IDF权重矩阵看名字你也应该知道:
        Tfidf-Transformer + Count-Vectorizer  = Tfidf-Vectorizer下面的代码一步到位,把上面的两个步骤一次性全部完成值得注意的是,CountVectorizer()和TfidfVectorizer()里面都有一个成员叫做vocabulary_(后面带一个下划线)这个成员的意义,与我们之前在构建Bunch对象时提到的自己定义的那个vocabulary的意思是一样的,相当于词向量空间的坐标轴。显然,我们在第45行中创建tfidfspace中定义的vocabulary就应该被赋值为这个vocabulary_他俩还有一个叫做vocabulary(后面没有下划线)的参数,这个参数和我们第45中讲到的意思是一样的。那么vocabulary_和vocabulary的区别是什么呢?
        vocabulary_:是CountVectorizer()和TfidfVectorizer()的内部成员,表示最终得到的词向量空间坐标
        vocabulary:是创建CountVectorizer和TfidfVectorizer类对象时,传入的参数,它是我们外部输入的空间坐标,不写的话,函数就从输入文档中自己构造。一般情况它俩是相同的,不一般的情况没遇到过。
        '''
        #构建一个快乐地一步到位的玩意儿,专业一点儿叫做:使用TfidfVectorizer初始化向量空间模型
        #这里面有TF-IDF权重矩阵还有我们要的词向量空间坐标轴信息vocabulary_
        vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5)
        '''关于参数,你只需要了解这么几个就可以了:
        stop_words:传入停用词,以后我们获得vocabulary_的时候,就会根据文本信息去掉停用词得到
        vocabulary:之前说过,不再解释。
        sublinear_tf:计算tf值采用亚线性策略。比如,我们以前算tf是词频,现在用1+log(tf)来充当词频。
        smooth_idf:计算idf的时候log(分子/分母)分母有可能是0,smooth_idf会采用log(分子/(1+分母))的方式解决。默认已经开启,无需关心。
        norm:归一化,我们计算TF-IDF的时候,是用TF*IDF,TF可以是归一化的,也可以是没有归一化的,一般都是采用归一化的方法,默认开启.
        max_df:有些词,他们的文档频率太高了(一个词如果每篇文档都出现,那还有必要用它来区分文本类别吗?当然不用了呀),所以,我们可以设定一个阈值,比如float类型0.5(取值范围[0.0,1.0]),表示这个词如果在整个数据集中超过50%的文本都出现了,那么我们也把它列为临时停用词。当然你也可以设定为int型,例如max_df=10,表示这个词如果在整个数据集中超过10的文本都出现了,那么我们也把它列为临时停用词。
        min_df:与max_df相反,虽然文档频率越低,似乎越能区分文本,可是如果太低,例如10000篇文本中只有1篇文本出现过这个词,仅仅因为这1篇文本,就增加了词向量空间的维度,太不划算。当然,max_df和min_df在给定vocabulary参数时,就失效了。
        '''
        #此时tdm里面存储的就是if-idf权值矩阵
        tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
        tfidfspace.vocabulary = vectorizer.vocabulary_
        _writebunchobj(space_path, tfidfspace)
        print "if-idf词向量空间实例创建成功!!!"
    if __name__ == '__main__':
        stopword_path = "train_word_bag/hlt_stop_words.txt"#停用词表的路径
        bunch_path = "train_word_bag/train_set.dat"  #导入训练集Bunch的路径
        space_path = "train_word_bag/tfdifspace.dat"  # 词向量空间保存路径
        vector_space(stopword_path,bunch_path,space_path)




    上面的代码运行之后,会将训练集数据转换为TF-IDF词向量空间中的实例,保存在train_word_bag/tfdifspace.dat中,具体来说,这个文件里面有两个我们感兴趣的东西,一个是vocabulary,即词向量空间坐标,一个是tdm,即训练集的TF-IDF权重矩阵。



    接下来,我们要开始第5步的操作,设计分类器,用训练集训练,用测试集测试。在做这些工作之前,你一定要记住,首先要把测试数据也映射到上面这个TF-IDF词向量空间中,也就是说,测试集和训练集处在同一个词向量空间(vocabulary相同),只不过测试集有自己的tdm,与训练集(train_word_bag/tfdifspace.dat)中的tdm不同而已。

    同一个世界,同一个梦想。

    至于说怎么弄,请看下节。



    5,分类器

    这里我们采用的是朴素贝叶斯分类器,今后我们会详细讲解它。

    现在,你即便不知道这是个啥玩意儿,也一点不会影响你,这个分类器我们有封装好了的函数,MultinomialNB,这玩意儿获取训练集的权重矩阵和标签,进行训练,然后获取测试集的权重矩阵,进行预测(给出预测标签)。



    下面我们开始动手实践吧!



    首先,我们要把测试数据也映射到第4节中的那个TF-IDF词向量空间上:

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    """
    @version: python2.7.8 
    @author: XiangguoSun
    @contact: sunxiangguodut@qq.com
    @file: test.py
    @time: 2017/2/8 11:39
    @software: PyCharm
    """
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    # 引入Bunch类
    from sklearn.datasets.base import Bunch
    import cPickle as pickle
    from sklearn.feature_extraction.text import TfidfVectorizer
    def _readfile(path):
        with open(path, "rb") as fp:
            content = fp.read()
        return content
    def _readbunchobj(path):
        with open(path, "rb") as file_obj:
            bunch = pickle.load(file_obj)
        return bunch
    def _writebunchobj(path, bunchobj):
        with open(path, "wb") as file_obj:
            pickle.dump(bunchobj, file_obj)
    def vector_space(stopword_path,bunch_path,space_path,train_tfidf_path):
        stpwrdlst = _readfile(stopword_path).splitlines()
        bunch = _readbunchobj(bunch_path)
        tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
        #导入训练集的TF-IDF词向量空间
        trainbunch = _readbunchobj(train_tfidf_path)
        tfidfspace.vocabulary = trainbunch.vocabulary
        vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5,vocabulary=trainbunch.vocabulary)
        tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
        _writebunchobj(space_path, tfidfspace)
        print "if-idf词向量空间实例创建成功!!!"
    if __name__ == '__main__':
        stopword_path = "train_word_bag/hlt_stop_words.txt"#停用词表的路径
        bunch_path = "test_word_bag/test_set.dat"   # 词向量空间保存路径
        space_path = "test_word_bag/testspace.dat"   # TF-IDF词向量空间保存路径
        train_tfidf_path="train_word_bag/tfdifspace.dat"
        vector_space(stopword_path,bunch_path,space_path,train_tfidf_path)


    你已经发现了,这段代码与第4节几乎一模一样,唯一不同的就是在第39~41行中,我们导入了第4节中训练集的IF-IDF词向量空间,并且第41行将训练集的vocabulary赋值给测试集的vocabulary,第43行增加了入口参数vocabulary,原因在上一节中都已经说明,不再赘述。

    考虑到第4节和刚才的代码几乎完全一样,因此我们可以将这两个代码文件统一为一个:



    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    """
    @version: python2.7.8 
    @author: XiangguoSun
    @contact: sunxiangguodut@qq.com
    @file: TFIDF_space.py
    @time: 2017/2/8 11:39
    @software: PyCharm
    """
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    from sklearn.datasets.base import Bunch
    import cPickle as pickle
    from sklearn.feature_extraction.text import TfidfVectorizer
    def _readfile(path):
        with open(path, "rb") as fp:
            content = fp.read()
        return content
    def _readbunchobj(path):
        with open(path, "rb") as file_obj:
            bunch = pickle.load(file_obj)
        return bunch
    def _writebunchobj(path, bunchobj):
        with open(path, "wb") as file_obj:
            pickle.dump(bunchobj, file_obj)
    def vector_space(stopword_path,bunch_path,space_path,train_tfidf_path=None):
        stpwrdlst = _readfile(stopword_path).splitlines()
        bunch = _readbunchobj(bunch_path)
        tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
        if train_tfidf_path is not None:
            trainbunch = _readbunchobj(train_tfidf_path)
            tfidfspace.vocabulary = trainbunch.vocabulary
            vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5,vocabulary=trainbunch.vocabulary)
            tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
        else:
            vectorizer = TfidfVectorizer(stop_words=stpwrdlst, sublinear_tf=True, max_df=0.5)
            tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
            tfidfspace.vocabulary = vectorizer.vocabulary_
        _writebunchobj(space_path, tfidfspace)
        print "if-idf词向量空间实例创建成功!!!"
    if __name__ == '__main__':
        stopword_path = "train_word_bag/hlt_stop_words.txt"
        bunch_path = "train_word_bag/train_set.dat"
        space_path = "train_word_bag/tfdifspace.dat"
        vector_space(stopword_path,bunch_path,space_path)
        bunch_path = "test_word_bag/test_set.dat"
        space_path = "test_word_bag/testspace.dat"
        train_tfidf_path="train_word_bag/tfdifspace.dat"
        vector_space(stopword_path,bunch_path,space_path,train_tfidf_path)




    哇哦,你好棒!现在连注释都不用,就可以看懂代码了。。。



    对测试集进行了上述处理后,接下来的步骤,变得如此轻盈和优雅。



    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    """
    @version: python2.7.8 
    @author: XiangguoSun
    @contact: sunxiangguodut@qq.com
    @file: NBayes_Predict.py
    @time: 2017/2/8 12:21
    @software: PyCharm
    """
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    import cPickle as pickle
    from sklearn.naive_bayes import MultinomialNB  # 导入多项式贝叶斯算法
    # 读取bunch对象
    def _readbunchobj(path):
        with open(path, "rb") as file_obj:
            bunch = pickle.load(file_obj)
        return bunch
    # 导入训练集
    trainpath = "train_word_bag/tfdifspace.dat"
    train_set = _readbunchobj(trainpath)
    # 导入测试集
    testpath = "test_word_bag/testspace.dat"
    test_set = _readbunchobj(testpath)
    # 训练分类器:输入词袋向量和分类标签,alpha:0.001 alpha越小,迭代次数越多,精度越高
    clf = MultinomialNB(alpha=0.001).fit(train_set.tdm, train_set.label)
    # 预测分类结果
    predicted = clf.predict(test_set.tdm)
    for flabel,file_name,expct_cate in zip(test_set.label,test_set.filenames,predicted):
        if flabel != expct_cate:
            print file_name,": 实际类别:",flabel," -->预测类别:",expct_cate
    print "预测完毕!!!"
    # 计算分类精度:
    from sklearn import metrics
    def metrics_result(actual, predict):
        print '精度:{0:.3f}'.format(metrics.precision_score(actual, predict,average='weighted'))
        print '召回:{0:0.3f}'.format(metrics.recall_score(actual, predict,average='weighted'))
        print 'f1-score:{0:.3f}'.format(metrics.f1_score(actual, predict,average='weighted'))
    metrics_result(test_set.label, predicted)



    出错的这个,是我故意制造的,(因为实际分类精度100%,不能很好的说明问题)

    效果图:



     

    当然,你也可以采用其他分类器,比如KNN



    6,评价与小结

    评价部分的实际操作我们已经在上一节的代码中给出了。这里主要是要解释一下代码的含义,以及相关的一些概念。

    截止目前,我们已经完成了全部的实践工作。接下来,你或许希望做的是:

    1,分词工具和分词算法的研究

    2,文本分类算法的研究

    这些内容,博主会在今后的时间里,专门研究并写出博文。



    整个工程的完整源代码到这里下载:

    https://github.com/sunxiangguo/chinese_text_classification

    需要说明的是,在工程代码和本篇博文中,细心的你已经发现了,我们所有的路径前面都有一个点“. /”,这主要是因为我们不知道您会将工程建在哪个路径内,因此这个表示的是你所在项目的目录,本篇博文所有路径都是相对路径。因此你需要自己注意一下。工程里面语料库是空的,因为上传资源受到容量的限制。你需要自己添加。

    7,进一步的讨论

    我们的这些工作究竟实不实用?这是很多人关心的问题。事实上,本博文的做法,是最经典的文本分类思想。也是你进一步深入研究文本分类的基础。在实际工作中,用本文的方法,已经足够胜任各种情况了。


    那么,我们也许想问,有没有更好,更新的技术?答案是有的。未来,博主会集中介绍两种技术:
    1.利用LDA模型进行文本分类
    2.利用深度学习进行文本分类


    利用深度学习进行文本分类,要求你必须对深度学习的理论有足够多的掌握。
    为此,你可以参考博主的其他博文,
    例如下面的这个系列博文《卷积神经网络CNN理论到实践》。
    这是一些列的博文。与网上其他介绍CNN的博文不同的是:
    1. 我们会全方位,足够深入的为你讲解CNN的知识。包括很多,你之前在网上找了很多资料也没搞清楚的东西。
    2. 我们会利用CNN做文本分类的实践。
    3. 我们会绘制大量精美的示意图。保证博文的高质量和美观。






    8,At last





    welcome!

    Xiangguo Sun 

    sunxiangguodut@qq.com 

    http://blog.csdn.net/github_36326955 



    Welcome to my blog column: Dive into ML/DL!

    (click here to blog column Dive into ML/DL)

    这里写图片描述


    I devote myself to dive into typical algorithms on machine learning and deep learning, especially the application in the area of computational personality.

    My research interests include computational personality, user portrait, online social network, computational society, and ML/DL. In fact you can find the internal connection between these concepts: 

    这里写图片描述

    In this blog column, I will introduce some typical algorithms about machine learning and deep learning used in OSNs(Online Social Networks), which means we will include NLP, networks community, information diffusion,and individual recommendation systemApparently, our ultimate target is to dive into user portrait , especially the issues on your personality analysis.


    All essays are created by myself, and copyright will be reserved by me. You can use them for non-commercical intention and if you are so kind to donate me, you can scan the QR code below. All donation will be used to the library of charity for children in Lhasa.



    分答头条

    $
    0
    0

    1922年,心理学家卡尔·荣格在瑞士一个名为伯林根的村庄建起居所。最开始他建起一座简单的两层石头房子,将其称作塔楼。

    “在休息室时,我可以独处。”荣格这样评述这个房间。

    img

    荣格在伯林根的塔楼

    尽管我们很容易将伯林根塔楼看作是度假屋,但是回顾荣格的职业生涯,会发现这座湖边居所,并非为躲避工作而建。

    1922年,荣格买下这片地产的时候还无暇度假。仅仅一年之前,在1921年,他发表了重要著作《心理类型学》,总结了长久以来与其曾经的好友兼导师西格蒙德·弗洛伊德之间思想的诸多差异。

    在20世纪20年代,对弗洛伊德的思想提出质疑是很大胆的举动。想要支撑自己的著作,荣格需要保持清醒的状态,创作出一系列有见地的文章和著作,进一步支持和发展分析心理学。

    他后来也被称作分析心理学的创始人。

    当然,他最终的成功背后有很多原因,但是我的兴趣点会落在下述这项技能:

    1、深度工作(Deep Work)

    深度工作指的是在无干扰的状态下,专注进行职业活动,使个人的认知能力达到极限。这种努力能够创造新价值,提升你的技能,而且难以复制。

    其实,如果探究一番远近历史中一些重要人物的生活,你会发现深度工作在他们身上是非常普遍的。

    比如16世纪的散文家米歇尔·德·蒙田,早在荣格之前就在远离自己法国城堡石墙的南塔楼区,建起了一座私人图书馆;

    而马克·吐温的《汤姆索亚历险记》,大部分都在纽约库阿里农场的一间小屋里完成,当时他在那里消夏。

    img

    左为蒙田,右为马克·吐温

    当然,深度工作不仅仅局限于历史人物和厌恶电脑之人。

    在具有重大影响力的人物身上,深度工作的普遍存在现象很值得强调,因为这种现象与当代大多数知识工作者的行为形成了鲜明的对

    比,这个群体很快淡忘了深度工作的价值。

    知识工作者之所以不再了解深度工作,原因显而易见——网络工具。

    网络工具是一个非常广的门类,包括各种通讯服务、社交网络和娱乐信息网站。这些工具的兴起,辅以智能手机和可联网办公电脑的广泛使用,将大多数知识工作者的注意力肢解得支离破碎。

    2012年,麦肯锡的一项调研发现,知识工作者平均每周有超过60%的工作,是进行电子沟通和网络搜索,工人们则有将近30%的时间,只是用于阅读和回复电子邮件。

    这种注意力支离破碎的状态,不可能带来深度工作,深度工作要求长时间无干扰的思考。

    然而与此同时,现代知识工作者也并没有游手好闲。事实上,他们称自己和以前一样忙碌。这种矛盾是由何产生的?

    另外一种类型的努力可以很好地解释个中原由,这种努力恰与深度工作的理念相悖:

    2、浮浅工作(Shallow Work)

    浮浅工作说的是对认知要求不高、事务性的任务,往往在受到干扰的情况下开展。此类工作通常不会为世界创造太多新价值,且容易复制。

    换言之,在网络工具的时代,知识工作者越来越多地用浮浅工作——像人工网络路由器一样不断地收发电子邮件,不断被小事扰乱心神——替代了深度工作。

    诸如研究新的商务策略或撰写重要的经费申请等重大工作,本可很好地受益于深度思考,却也在受到干扰之后变得支离破碎,完成的质量不会太好。

    对于深度工作更不利的是,越来越多的证据显示,向浮浅工作发展的趋向并非是很容易就能转变的。在极度浮浅的状态下度过足够的时间,将永久性降低自己深度工作的能力。

    img

    “网络似乎剥夺了我专注和沉思的能力。”

    记者尼古拉斯·卡尔2008年在《大西洋月刊》上发表的一篇文章中坦言,“不仅只有我是这样。”

    卡尔由这个论点出发,写成了一本书,名为《浅薄:互联网如何毒化了我们的大脑》,后入围了普利策奖终选名单。为了写作《浅薄》一书,卡尔恰如其当地搬到一间小木屋里,强迫自己与世隔绝。

    卡尔·荣格的伯林根塔楼成为他保持深度思考的场所,之后他才将技能加以应用,产出令人讶异的独创性成果,以至改变了世界。

    如果我们能够共同努力构建自己的伯林根塔楼,培养在日益浮躁的世界里创造真实价值的能力,就能够认识到数代人中最高产和最重要的人物所认可的真理:

    深度的生活是优质的生活。

    img

    文章节选自《深度工作》,著者:[美]卡尔·纽波特(Cal Newport)译者:宋伟,出 版:后浪丨江西人民出版社;首发于微信公众号“后浪”(ID:hinabook),为优化阅读体验,转载时有删减。

    女生学计算机,没出路吗? - 知乎

    $
    0
    0

    你若问我,妹子学计算机是不是更难,我会说:是。因为妹子总有捷径和后路可选。

    可选而不选,这才是最难的。

    我用血泪的教训提醒你:妹子学计算机,不要想着自己的性别。

    这是我能想到的,给你最好的建议!



    如果总想着自己是个妹子,只有一个后果:遇到事就想利用性别找男生帮忙。这会让你陷入可悲的恶性循环。

    我也不要什么面子了,明确的告诉你,我就是这么过来的!现在追悔莫及,如果让我重新学习,我一定凡是亲力亲为,因为当初走的捷径,现在全还回来了。



    在计算机专业,你只要稍稍想着“我是个妹子,来帮我吧”,就真的会有不错的男生来帮你(当然我很感激当初帮助我的人啦,尤其是老曹么么哒。)。你慢慢就会养成习惯:遇到问题就去找男生帮忙!自己独立思考的时间越来越少,承受压力的本事越来越小。

    当你没本事,抗压差的时候,身为妹子的你,自然而然的就想着“要不嫁人吧”,“嗨呀,大不了找个大神当男朋友”。 于是你在抱大腿的路上,越走越远。

    你用“女生”做关键词提出这个提问,说明你已经对自己的性别不自信了。 这样日后学习难免有抱大腿的危险思想,我一定要把这种危险的思想在源头给掐断!!!

    (什么?你是男的也靠大腿? 那你真是条铁咸鱼无误了,翻都翻不动。)



    比自己轻视自己更危险的是:男生根本不会提醒你这么做不对,因为你是个妹子。

    在所有男生居多的专业里,男生对妹子的态度都是战略上无视,战术上包容的,切记切记!



    在学校还能尝一尝小公主的滋味,但是工作以后!!大家都这么忙!他们可没空管你了!!他们对你的要求突然拉倒跟他们一个水平线了!除非你给钱!或者他看上你了!不然你算老几啊!!这时候你再做不好,他们只会默默鄙视你,然后得出“女人果然不行”的结论!!

    所以出来混迟早要还的!



    因此,“我知道自己是专业/行业里稀缺的性别”是绝对不可取的学习态度!



    我当然知道不是所有妹子学计算机都是我这样,但我这样的一点都不少!!

    我见过漂亮妹子为了蹭项目背着男朋友喊别人老公的,我见过学渣妹子找男朋友以绩点为标准的。

    但我也见过妹子每天扎个简洁的马尾认真听课绩点4.0的,我也见过妹子首面G家就收到offer的。



    忘了自己的性别,大家都五五开,不要想太多。

    至于计算机出路问题,你把“女生”俩字去掉,自然就有答案了。





    答案刚完成的几分钟,评论有嘲讽,有同情。出于自尊心,评论关闭了。

    我不需要同情,别人也不配嘲讽我。

    国内人工智能公司在国际大赛上拿了一堆第一,但你知道他们到底在比什么吗?

    $
    0
    0

    作为人工智能的一个重要分支领域之一,计算机视觉在商业化上已经取得了不可小觑的成绩,这同时也让商业群体开始关注到学界在计算机视觉领域的研究。

    此时此刻,在意大利的威尼斯,刚刚结束的 2017 年国际计算机视觉大会上,来自中国的旷视科技研究院在 COCO 和 Places 竞赛中参加了 4 项比赛,并拿下了三项第一,一项第二,领先于微软、Facebook 和 Google 等科技巨头。

    关注到这一优秀的成绩,极客公园第一时间通过远程语音采访了远在威尼斯的旷视科技参赛团队的带头人姜宇宁,请他来为我们科普一下这场备受行业瞩目的竞赛。


    两个大赛:一个针对东西,一个针对背景

    首先介绍一下 COCO 和 Places 这两个竞赛。

    COCO 的全称是常见物体图像识别(Microsoft Common Objects in Context),起源于微软出资标注的一个数据集,与此前著名的 ImageNet 比赛同样被视为是计算机视觉领域最受关注和最权威的比赛之一,也是目前该领域在国际上唯一能汇集 Google、微软、Facebook 以及国内外顶尖院校和优秀创新企业的大赛。

    相较于更关注整体图像分类的 ImageNet,COCO 重在图像中的物体检测,打个比方,对于一张几条狗的照片,ImageNet 更注重参赛者能否将这张图片归类到「狗图」那一类,而 COCO 则注重于你能否识别出图片中的动物是不是狗,有几条,分别出现在图片上的什么位置等。

    目前 COCO 已经举办到了第三届,前两届的冠军分别是微软和 Google。

    今年的 COCO 比赛包含 4 个子项,分别是 物体检测物体分隔人体关键点检测和背景语义检测,旷视科技参加了前三项的比赛,姜宇宁也向我们解释了这几项比赛的具体内容。

    物体检测在上面已经有了基本的介绍,在计算机的交互界面上主要表现为用矩形框将物体框住。

    物体分割则是在物体检测上更进一步,不仅仅要确定物体在某一区域,还要对物体在图片上的形状做一个更加细节性的定位。接着拿上面的「狗图」举例,在物体分割的比赛中,参赛选手要让计算机识别出每条狗在图片上的具体形状,最终表现在二维图片上的效果类似于画图中的「描边」。

    人体关键点检测技术主要针对于图片中的人体,计算机不仅仅要识别出人的位置、确定在图上的细节形状,还要确定人的手、肩膀、腿等关键点的具体位置。

    不同于 COCO 对物体的着重,与之同期举办的 Places 竞赛则更注重场景的检测。今年的 Places 开放了 3 个子项, 场景分割物体分割以及 边缘检测。旷视科技参加了物体分割,并在挑战中击败 Google,赢得了该问题的冠军。

    据姜宇宁解释, 场景分割即把物体和背景分割开来,继续「狗图」的例子,假使两条狗在一起,物体分割就需要把两条狗分割开来,而场景分割「只需要描 1 个边就可以了」。

    至于 边缘检测,则是对不同物体的边缘进行「分辨」,假使「狗图」中还有一只猫,那么猫与狗的边缘和狗与狗的边缘在这项任务中是不同的。姜宇宁提到,这项技术比较难,今年只有 6 支队伍参加,并且没有一支队伍取得了「看得过去的成绩」,所以最后并没有对这项比赛进行颁奖。


    这些高大上的技术都有什么用?

    姜宇宁提到,其实 物体检测是「所有计算机视觉的技术中最基本、最重要但却最不容易被大众察觉到的一个关键步骤」。

    就好像吃饭前要洗手一样,现在计算机视觉一个比较热门的应用是人脸识别,而在人脸识别系统中第一个步骤就是人脸检测,「第一步就是把人脸找到并抠出来,再进行人脸识别。」

    在旷视最重要的产品 Face++上,这样的基础性技术就非常重要。「在安防的视频结构化的场景中,行人、车牌、车辆的目标分析,第一步就是找到目标;同样在无人驾驶这样的应用中,物体检测技术也是一个非常非常基本的条件。」

    人体关键点检测技术则更多地应用于行为动作分析和人机交互的场景。

    比如在监控和无人超市这样的场景中,机器需要知道顾客有没有拿东西,第一步就是检测顾客的手「有没有伸出去」,对机器来说,这需要它确定摄像头拍摄下的顾客的手上一帧在哪,现在这一帧在哪,从而分析出顾客的手是否发生了移动。

    在很多体感游戏机上, 人体关键点检测技术就得到了广泛的应用。

    总的来说,竞赛的各项技术都是人工智能应用中的基础型技术,用姜宇宁的话来说,就是人工智能企业的「内功」。

    从某种程度上来说,包括旷视、商汤在内的多家国内企业和高校在 COCO 和 Places 这种国际权威的大赛上取得不错的成绩,其实是国内人工智能行业硬实力的体现。当极客公园问到姜宇宁为什么要出去参加这项比赛时,他说道,「最重要的是检验自己的『内功』,在比赛中发现平时的研究是否有疏漏不足的地方,其次是要锻炼年轻的研究队伍,这次参加比赛的团队平均年龄大概只有 22 岁,很多人还是本科在读。」

    「没想过要出去秀一下肌肉吗?」

    姜宇宁笑着回答说,「这也算是原因之一吧。」


    头图来源:视觉中国

    责任编辑:早优夫斯基


    自动驾驶落地,究竟被什么「绑」住了脚?

    $
    0
    0

    编译 | 高静宜 项文虎 邱陆陆

    来源 | wired


    自动驾驶汽车的行驶范围将不再局限于测试跑道或是平静的郊区街道,它们出现在美国的纽约、旧金山以及匹兹堡等地,参与到真实世界的交通中去,也进驻欧洲、韩国、新加坡和日本等国家,在人类的包围中磨练自身的技能,还准备用它们的机器人系统完善混乱、无秩序的街道交通。


    在类似波士顿这样的城市学会如何开车是非常具有挑战性的,那里有颇具创意的左转弯以及貌似随意的让行规则。不过,与发展中国家中司机的侵略性驾驶方式以及城市错综复杂的道路相比,这些都不足为惧了。跟那些对交通信号、警示和限速标志没有一点尊重意识的司机比起来,Patriots 橄榄球队(球队以横冲直撞的打法著称)的球迷看起来都算是正常人了。


    全世界范围内,有许多道路没有车道,交叉路巨大而混乱。在这样的地方,行人的动作支配着道路的交通状况,每个司机都需要根据其他人的行为当场调整自己的驾驶策略,根本不会考虑到交通规则对自己的约束。


    这些不正规的交通系统存在于许多地区,造成了严重损失。根据 2013 年世界卫生组织的最新数据,全世界有 50 个国家拥有这种极度危险的道路,其中有 44 个国家位于非洲或中东地区。2013 年,这些国家的交通事故死亡人数接近 25 万,占当年世界交通事故死亡人口总量的五分之一。


    然而,事实是这些可以受益于自动驾驶汽车的地区不可能在短期内获得这种技术。


    「很多我们现在正在自动驾驶领域做的工作可能并不会在第三世界国家成功落地。」密歇根大学福特自动驾驶汽车中心主任 Ram Vasudevan 说道。


    不守交规的人类车辆,无所畏惧的行人,随时发生的混乱状况


    自动驾驶技术需要理解道路上每一个人和每一样事物的行动意图和行驶轨迹,包括车辆、自行车、行人、建筑工人、玩耍的孩童、宠物,甚至是玩具枪意外发出的飞镖。在驾驶环境中,人们遵循着一套既定的规则,而自动驾驶汽车在环境中的期望行为则需要受到法律的约束。


    所制定的交通规则越少,意图的预测能力就越重要。因为有人可能会做出一些意想不到的举动,所以汽车不能单纯地依赖共通的准则支配自己的行为。例如,只有在道路上其他人都遵守车道线规则的情况下,司机把车辆停放在车道线内才有用,如若不然,还是会存在危险。


    与郊区以及美国城市相比,中东和非洲国家的驾驶环境是比较随意的。在黎巴嫩,看到车辆行驶在错误的车道上,闯红灯,甚至是出于恶作剧的心理,无视交通法规在宽阔的马路上呈「Z 字」前行,这些都是很常见的。


    「这里没有规矩,一切都是可能的。」美国贝鲁特大学的计算机视觉专家及工程教授 Daniel Asmar 说道,「人们可以很好地应对这样的情况,哪怕他们也会因此变得沮丧或是向对方狂按喇叭。」而对于电脑来说,这样混乱的情况是非常巨大的挑战。


    Vasudevan 认为,即便是在相对有序的环境中,突发混乱也会造成交通堵塞甚至事故的发生,例如自动驾驶汽车在高速公路上并道的时候犹豫的时间过长。这可能是汽车软件的原因,为了安全起见不愿在超速车的前面合并,或是需要更多的时间掌握周围场景及其他车辆的意图。如果把车放在一条不存在或是可以忽略指示路牌、交通信号以及让车规则的道路上,通常需要一定的反应时间来提升系统的性能以应对当前境遇。


    更重要的是,自动驾驶汽车还需要地图数据的支持,而世界上许多地区都还无法提供这样的信息。自动驾驶技术需要覆盖一切街道信息的详细地图,包括通过街道的限制高度、因临时施工而设置的绕行道路以及街道标志与交通信号在三维空间中的精确位置。自动驾驶车队已经在一些城市中行驶,不断捕获数据,并对其进行更新升级,研制了这样的地图。


    在类似黎巴嫩这样的地方,谷歌地图和苹果地图都有一些基本的错,而数据缺失又是一个巨大的劣势。就算这个世界上存在无比详尽的地图,也需要极其密集的维护。Asmar 说,「在一个结构化的环境中,你无需经常进行维护,因为发生改变的东西不会太多。然而在一个非结构化的、万事万物都在改变的环境中,你可以想象需要多少次重建这个平台。这是一项非常艰巨的任务。」


    少数几个中东的富裕国家已经开始走向自动驾驶。在以色列,一些公司主持开发了极为重要的一些自动驾驶软件,而这个国家在上个月开放了第一条供无人车测试的公路。在迪拜,一趟低速十座无人班车从去年开始在城市的沿河商业区运行。市政府官员称,目标是在 2030 年让 1/4 的本地交通无需司机。另外,迪拜的警察也计划在今年年底推出小型无人驾驶自动巡逻车。


    但印度和中国应该是两个同时存在驾驶乱局和致力于研发自动驾驶车辆的本地公司的国家。无疑,他们的努力需要跨越更多障碍。据彭博社报道,印度公司 Tata 在班加罗尔城外修建了一段测试公路,用来模仿本地的路况,自动驾驶车辆需要和无所畏惧的行人与迷路的牛争抢路权。公司要走的路还很长:一位 Tata 公司的高级副总裁告诉彭博社,由于印度道路上的「车辆」千奇百怪的形状和尺寸,Tata 的计算机视觉系统目前无法对其中的 15% 进行有效识别。(去年,Uber 前 CEO Travis Kalanick 访问印度的时候曾经开玩笑说印度会是「地球上最后一个实现自动驾驶的国家」,因为「你见过当地人是怎么开车的吗?」)


    中国的百度,也在公开进行自动驾驶开发计划。百度与全世界超过 50 家公司进行合作开发自动驾驶软件系统。在此前的一个演示视频里,百度 CEO 李彦宏坐在一辆自动驾驶汽车中,行驶在北京的马路上,一路上作出了一些不安全操作。由于现阶段自动驾驶汽车在中国还不能合法上路,中国警方称,会调查李先生的行为是否触犯了任何法律法规。(出于工作流失考虑,印度也在考虑出台类似的对自动驾驶车上路的禁令。)尽管面临重重监管屏障,百度总裁张亚琴仍然向彭博社表达了他的信心,称公司的无人驾驶汽车「最早明年就能上路」。


    中国的网约车公司滴滴出行采取了更为审慎的做法。尽管公司今年在加州开设了开发无人驾驶技术的办公室,公司总裁柳青最近在接受 Charlie Rose 的采访时出人意表地说,「颠覆性」地切换到自动驾驶是十分危险的。「我认为,人们应该更关注这项技术有多安全而不是有多快能落地。」柳青说。


    在中国,自动驾驶汽车不仅要学会处理与其他车之间的关系,还要学会与电动车以及视交通规则为无物的行人相处,一位滴滴发言人称,自动驾驶汽车需要理解不同区域的交通标志与交通信号的区别,尤其是在中国,这些标志不像在美国或者欧洲那么规范。因此,滴滴的规模为公司带来了很大优势。公司表示,人类司机每天提供 2500 万车次的服务,生成超过 70 TB 可用于自动驾驶技术开发的数据。


    不同地区驾驶环境不同,自动驾驶公司提供的却是相同的数据和软件


    目前,大多数公司测试无人驾驶车辆的方法还是在可控路段上给汽车设置很多意外情景。例如 Waymo 就在自己的神秘基地 Castle 里训练它们的车,人类测试员会恶意阻挡高速行驶的汽车、从视野盲区的车道里并道、向车辆扔篮球等等,用各种测试方式改善车辆的反应。


    但是,由于人工智能的训练基于许多假设条件,所以换一个场景时常会失败。研究发现,在白种人测试对象上训练的面部识别算法,应用到非裔美国人的面部识别时效果不佳,而在东亚人测试对象上训练的算法应用到白种人上同样表现不佳。这种差异可能在自动驾驶汽车领域也存在,所以相关软件的测试总是小心翼翼,也考虑到多种极端的情况。


    不过,尽管人们驾驶的方式存在一些地区差异,但制造商可不太可能为各个地区配置特定的驱动软件。「目前而言,在不同的文化背景下,使用的确实是相同的数据公式和软件。」密歇根大学教授、福特中心联席董事 Matthew johnson - Roberson 说。


    最重要的是,训练中的汽车会对所有从外界收集到的信息都作出回应。一位优步发言人表示,为了提升软件的适应性,优步正在多个城市进行测试,收集不同条件不同时段下的测试信息,目前行驶里程已经超过 100 万英里。


    但是,即使自动驾驶软件能够理解不守规矩的司机,也能预测他们如何触犯法律,自动驾驶汽车也有可能受到限制。一位发言人表示,优步的汽车将始终遵守当地的交通规范。博世自动驾驶的高级副总裁 Stephan Hoenle 对此表示赞同:「在不违反当地交通规则的情况下,自动驾驶的部署会更加顺畅。」一辆自动驾驶汽车的行车风格可能因需求和期望有所不同,但违反法律并不是一种正确的选择——对于制造商来说,这是一项巨大的责任。


    问题在于,在某些地方,严格遵循法律条文可能比模仿违法的人类司机更危险。当急性子的通勤者在高峰时段变道时,后面的车辆没有及时调整的话,就会导致接连的追尾。


    北美、欧洲和新加坡会远远甩开迫切需要自动驾驶的发展中国家


    对于那些每天努力提升自动驾驶性能的人而言,似乎并不急着探寻自动驾驶汽车的终极能力。「到目前为止,完全自动驾驶的汽车没有出现,对吧?」密歇根大学的 Johnson-Roberson 说,「从工程学的角度来看,我不知道有谁在研究这个问题,因为一些基本原理还没有实现。」


    推迟这些问题的探索,可能会让那些最需要自动驾驶技术的地区进展缓慢。Hoenle 声称,自动驾驶汽车将在世界各地都得到大规模应用,但他同时也承认,这还需要一段时间的发展。他还表示:「与美国和欧洲相比,其他地区的技术增长速度通常较慢。」


    但 MIT 感应城市实验室的主任 Carlo Ratti 预测,发展中国家最终将迎头赶上。他在一封电子邮件中写道:「每一项前沿的技术都可能会增加现有的社会差距。但是,随后的技术传播可能会引起有趣的『弯道超车』效应,从而减少这点差距。」


    例如,手机最初只面向富裕的西方人,但现在在非洲也非常普遍。如今,非洲的创业公司正在为移动银行和医疗服务提供新的想法,Ratti 说:「没有理由认为自动驾驶汽车会走不同的道路。」


    自动驾驶汽车的出现与「弯道超车」阶段之间的过渡区可能会有些长,因为它们必须适应周围环境,需要收集每条街道的特定的数据,如果设计不当,发展也有可能被遏制。


    开发商们回避了有关地区差异的问题,把问题留给了市场之外「增长曲线」。随着自动驾驶的核心技术在北美、欧洲和新加坡等地的快速发展,它可能会远远甩开那些最迫切需要这项技术的发展中国家。


    Viewing all 15900 articles
    Browse latest View live


    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>