-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathsearch.xml
417 lines (417 loc) · 394 KB
/
search.xml
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[程序媛如何利用业余时间月入过万]]></title>
<url>%2F2019%2F12%2F12%2Fway-to-money%2F</url>
<content type="text"><![CDATA[几个月前,我在github开了个repo,叫【Six-Figure-Plan】,六位数计划,寓意迈向月薪六位数的计划。当然是private repo,近期都不打算公开😋 为什么开这个repo?受身边人的影响,某次听到男友给员工开出了六位数的月薪,而那个员工跟我差不多年纪,对应的学历要求和工作难度强度都不高。为啥我还操着卖白粉的技术,拿着卖白菜的钱,是哪里出了问题?🤔还有身边的程序媛凭着开朗的social技能转行到产品经理又凭着出色的主持技能和英语交涉技能晋级成为CEO助理,有次恰巧又看到 Jenny 博主打卡月薪6位数,也是某公司CEO助理。这些人里面,无论是固定六位数还是一次性六位数,都让我由衷羡慕👏 当然我不想质疑自己走的路,相信筑建自己的技能壁垒和核心竞争力是正确走向,也曾在网上看到世界上其他优秀年轻人的百万计划,或者提早退休计划等等,很有意思,自己也搞一个程序员版本的。 Who am II’m nobody. 我就是个普通码农,本职搬砖,AI算法工程师。曾经以及现在是个金融经济类白痴,但这不妨碍我爱赚钱。大多数时候就是一个目光短浅,只想看着自己的余额噌噌噌往上涨,只想看着自己名下的资产越来越多的人嘿😜 从一个程序员的赚钱之路出发,欢迎集思广益,与广大网友一起迈进六位数行列。 What I want想从这个计划中获得什么 自信。没错,就是越有钱,越有底气。意义参考刘玉玲的“Fuck-You-Money”。 I value my time. 我现在的时间虽然看起来不值钱,但是我知道我有重要的事情要去做。 很多事情都不会去做,尤其浪费时间的那种。当水涨船高,看着自己的一小时越来越贵之后,会更加珍惜自己的时间。做什么事情的机会成本都会提高。因此会拒绝做很多浪费时间的事情,比如戒掉无效社交。 push myself. 天马行空,敢想敢做。 观察自我。 我想观察我的收入都跟什么有关,比如代码的积累,比如文字的积累,是否造成收入的增长。研究自己,研究自己的work part 我想坚持我自己的路,不想因为看起来的捷径,而放弃自己的核心竞争力。给自己一定时间,相信只要在自己的领域成为优秀的那拨,肯定能得到想要的。 Get what I deserve. 拼命干活,却没有相应收入,我拒绝做那样的人。 选择比努力重要,行业(大环境)比能力重要。 What I’ve Done业余的时间里,我到底为了这个计划做了哪些尝试?在这里做一个小小总结,在每种收入来源里简单介绍自己做了什么,以及体验感如何,它的优点和缺点。希望未来还有更精彩的故事和大家分享。 收入来源1:博客打赏那么就从经营这个博客开始说起吧。因为在程序员这个圈子里需要不断地学习,很多内容需要反复地看,和很多程序员一样,因此我就开始经营自己的小博客,偶尔发些机器学习笔记,或者日常生活感想。当初写的时候没觉得什么,也没规定自己一定要写多高深的技术稿,只是把自己所想慢慢记录下来。后面竟然有人看到我写的文章,而且真的有人给我打赏!这里截些高额的几条记录,大多数的都是1块2块钱,我也很感激,是我和看文章的人的一点点联系。 优点:鼓励自己写有价值的文章,随着积累后这个网站会拥有更大的影响力。网站这个本身也是自己的兴趣,希望能通过兴趣来养活兴趣,把网站的收入再投入到网站建设里去,形成一个良好的循环。缺点:如果是为了打赏而写文章,会影响文章客观角度和质量,不适合在这里写,更适合去公众号等流量大的平台。 收入来源2:博客加入谷歌联盟 Google AdSense在前几个月自己也捣腾了个谷歌的AdSense,就是在自己的网站上挂谷歌的广告,按照曝光量和点击量来赚美金。它要满100美金才能提现,个人感觉这真得靠流量,像我这种小破站就真的也不期望啥了,就这么挂着吧。 优点:不用管它,基本就一次搞定。当然也从侧面鼓励自己写更好的文章。缺点:影响网站美观,小破站实际上也搞不到什么收入的。 收入来源3:写大厂软文这个啥意思呢,就是现在自媒体非常地流行,每个机构都要经营自己的自媒体形象,也是其公关的一种方式。那么大厂尤其愿意在这方面砸钱。厂子做大了就要面子了嘛,都可以理解。这个我是偶然看到一个招稿的帖子,招科技垂直领域的写手,主要是AI/大数据/云计算/量子计算等方向。进入这个写手群之前需要审核,要求比较严格,大致概括为3类: 没有身份背书但是内容写的好的 有身份背书的(相关方向博士+就可以了) 既内容写的好又可以用身份背书的 审核通过后,对方有长文和短文两种形式,我选择写短文方向的,每篇就写个500来字的小短文,一般半小时到1小时搞定,一个字值2块钱这样。因为就是实打实的科技文,自己怎么想就怎么写,写完发过去也很少有改动。当然因为需要对技术有一定的了解,所以市场价比一般的职场软文要高一些。感觉也挺适合自己的。写一篇就给你打一次钱。下图是有时收到的到账信息,每次看到就当作是一个惊喜。 优点: 赤裸裸的金钱关系,事少钱多,还能进入优质群,群内的写手很多知乎大V或者被高度筛选后的,和自己匹配度较高。(虽然我很懒,除了对接人之外目前还没有主动social)缺点: 稿件需求不稳定,不是每个礼拜都有,看机会,看安排。 收入来源4:写专利这个属于公司的政策,写专利会给对应的奖金。这每个公司都有自己的一套规定。不展开细讲了。 优点: 可以在上班时间光明正大的写,也是业绩/声誉的一个展示方面。缺点: 专利审核时间非常非常长,所以有些奖金到账的周期也非常非常长。需要耐心。 收入来源5:合作推广(微博)除了经营这个小破站之外的唯一活跃在网上的渠道就是通过微博啦。通过分享一些学习日常和校园生活后,逐渐积累一些关注者followers,此时也有很多机构找上门想做推广合作。比如偶尔一阵子不上微博突然进去的私信都长这样。但是因为我个人对宣传/运营有自己的一套理解,我非常不喜欢很大众很常见的那些推广方式,因此我每每提交自己精心准备的推广文案后大多数都被退了回来,让我模仿其他博主那种千篇一律的写法,让我的精力我的时间付诸东流我真的很难过。前几次还能因为没接过推广的新鲜度给忍了,后面就不能够了,在微博没有二三十万的真粉,推广能拿到收入真的真的,还不能让我折腰再折腰。真的很少遇到心态开放的运营对接,主要他她们也有指标压力,算了,期待遇到和我“臭味相投”的那一个! 优点: 如果没有强迫症,如果能受的了对接的运营各种需求,偶尔赚零花钱也还行。缺点: 首先这个需要前期积累粉丝,另外接的广子自己要负责,要自己了解过的觉得好的,不能随便什么都接,我就推掉了很多淘宝店的,什么会计的考证的,而且由于个人原因,关于推广营销有自己的理解方式,很多别人要求的我都不想做不想写。当然推广接多了影响好感度的,是吧。 收入来源6:直播邀请因为在AI圈和开源社区活跃了一下子,就被大佬一眼相中了,哈,也没有啦。因为很多技术文的阅读量都非常低,但是我喜欢按我的角度去写技术的日常,甚至一些技术文,反响还不错,就被邀请做直播分享,第一次试水也比较成功,后面被介绍到其他的直播邀请。因为直播分享的经验还比较少,后面还会做更多的尝试! 优点: 时薪高,真的高。一场直播也就1个小时,一般会讲的收不住,友情赠送十几分钟半小时的。缺点: 这么好的机会不常有的,像我这种被动social的基本就接单靠缘分了。希望以后自己多多主动。主要我怕social多了我就飘了😂 收入来源7:合作课程靠朋友介绍,偶然得到的一个关于高校编程课程的编写合作,想来也是由于前面的经验积累,熟人觉得我做事还算靠谱,就帮我引荐给另一群更大的大佬。当然我能回报的就是踏踏实实干活了。 优点: 帮助梳理自己的知识体系,相当于再系统地复习一遍。对我来说,难度不算大,合作费用也不低,嗯!缺点: 暂时不多说了,等完成之后再跟大家聊。 收入来源8:比赛奖金优点: 拿奖金了肯定是比赛拿奖了,又能写进简历里加分,筑起自己的技能壁垒,又能拿钱。缺点: 拿这钱的难度的确有点高,拿了一次之后,不知道第二次是何时😭加油啊! 收入来源9:投资虽然不好意思讲,但是我实在没啥投资经验,我把我的钱都放在余额宝了😎,一天下来抵个伙食费是足够了。身边没有人通过股票收益,几乎全是亏损,于是自己也迟迟没有碰。男友非常不建议我碰这个,尤其我不懂的时候,但还是打算利用零花钱小小尝试下,这个不着急,等空下来再研究下。 男友的投资会高级很多(就是贵很多),比如买房,嗯,投资的那种,这一年的收益可能是我几十倍的工资😭。然后还有各种项目投资,有农业方向,有餐饮方向,他自己是金融方向的,一毕业就是四大的审计,做过金融产品,风控,金融业务等等。有时候蹭着他的项目我也跟投一点点。 最后,我想说,投资自己吧,别短视。 优点: 高风险高收益。缺点: 永远赚不到超出自己认知范围的钱。 写在最后:说几点自己的感悟和收获。 小时候都是用时间来买金钱,长大了就学会用钱买时间,而不是用时间换钱。用换来的时间实现自我增值。 专业的事情交给专业的人去做。网站这块,页面的维护一开始自己倒腾,但是再让我学前端语言对我来说是有时间成本的,仅仅为了维护网站学前端也不划算,所以有问题我会咨询我的前端朋友,而我自己就写些我擅长的内容就好。 我见过的赚钱的方式很多很多种,才明白到,作为一个程序员,低头写代码很重要,抬头看看这个世界也很重要。]]></content>
<tags>
<tag>money</tag>
</tags>
</entry>
<entry>
<title><![CDATA[预训练词向量的使用教程]]></title>
<url>%2F2019%2F12%2F11%2Fusing-pre-trained-word-embeddings-in-keras%2F</url>
<content type="text"><![CDATA[中文预训练资料下载 Chinese-Word-Vectors Tencent AI Lab Embedding Corpus for Chinese Words and Phrases 按照词向量不同的维度,可大可小,会有多个版本的预训练词向量,可以根据自己需求进行选择: 预训练词向量文件,一般比较大很难打开,大部分文件的格式由3个部分组成,以glove举例: 第一行有2个数字,左边的表示字典有多少个词,右边的表示词向量的维度 每行的第一个元素是词本身 每行的其它元素是词向量 预训练词向量的使用步骤 将所有语料转化为词索引序列(word_index)。所谓词索引就是为每一个词一次分配一个整数ID。 生成一个词向量矩阵(embedding_matrix)。第 i 列表示词索引为 i 的词的词向量。 将词向量矩阵载入Keras Embedding层,设置该层的权重不可再训练(也就是说在之后的网络训练过程中,词向量不再改变)。 数据预处理我们可以将语料样本转化为神经网络训练所用的tensor。所用到的Keras库是keras.preprocessing.text.Tokenizer和keras.preprocessing.sequence.pad_sequences。代码如下 1234567891011121314151617181920212223242526272829from keras.preprocessing.text import Tokenizerfrom keras.preprocessing.sequence import pad_sequences# 生成语料词索引序列tokenizer = Tokenizer(nb_words=MAX_NB_WORDS)tokenizer.fit_on_texts(texts)sequences = tokenizer.texts_to_sequences(texts)word_index = tokenizer.word_indexprint('Found %s unique tokens.' % len(word_index))data = pad_sequences(sequences, maxlen=MAX_SEQUENCE_LENGTH)# 标签格式转换labels = to_categorical(np.asarray(labels))print('Shape of data tensor:', data.shape)print('Shape of label tensor:', labels.shape)# 切分成测试集和验证集indices = np.arange(data.shape[0])np.random.shuffle(indices)data = data[indices]labels = labels[indices]nb_validation_samples = int(VALIDATION_SPLIT * data.shape[0])x_train = data[:-nb_validation_samples]y_train = labels[:-nb_validation_samples]x_val = data[-nb_validation_samples:]y_val = labels[-nb_validation_samples:] Embedding layer 设置接下来,我们从词向量预训练文件中解析出每个词和它所对应的词向量,并用字典(embedding_index)的方式存储。下面主要生成embedding_index: 12345678910111213embeddings_index = {}f = open(os.path.join(pretrained_wv_dir, 'Tencent_AILab_ChineseEmbedding.txt'))for line in f: values = line.split() # 因为每行的第一个元素是词,后面的才是词向量,因此将values[0]与values[1:]分开存放 word = values[0] coefs = np.asarray(values[1:], dtype='float32') embeddings_index[word] = coefsf.close()print('Found %s word vectors.' % len(embeddings_index)) 此时,我们可以根据得到的embedding_index生成上文所定义的词向量矩阵embedding_matrix: 123456789101112# embedding_matrix的维度 =(语料单词的数量,预训练词向量的维度)embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))for word, i in word_index.items(): # 根据语料生成的word_index来从embedding_index一条条获取单词对应的向量 embedding_vector = embeddings_index.get(word) if embedding_vector is not None: # words found in embedding index will be pretrained vectors. embedding_matrix[i+1] = embedding_vector # i+1 是为了处理OOV,使得预测时未见过的词的位置为0。当然如果不使用这种OOV的处理方式的话,这里的embedding_matrix[i+1]应该变成embedding_matrix[i],下同理。 else: # words not found in embedding index will be random vectors with certain mean&std.如果单词未能在预训练词表中找到,可以自己生成一串向量。 embedding_matrix[i+1] = np.random.normal(0.053, 0.3146, (1, embed_size))[0] # 0.053, 0.3146 根据统计,可以改变数值 现在我们将这个词向量举证加载到Embedding 层中,注意,我们设置trainable=False使得这个编码层不可再训练。 1234567from keras.layers import Embeddingembedding_layer = Embedding(len(word_index) + 1, EMBEDDING_DIM, weights=[embedding_matrix], input_length=MAX_SEQUENCE_LENGTH, trainable=False) 一个Embedding层的输入应该是一系列的整数序列,比如一个2D的输入,它的shape值为(samples,indices),也就是一个samples行,indices列的矩阵。 下面的是以函数形式写入dictionary模块的demo,跟上面的差不多,仅作参考: 12345678910111213141516171819202122232425262728293031323334353637383940from gensim.models import KeyedVectorsdef gen_embeddings_index(self, pretrained_wv_path): """ create a weight matrix for words in training docs 将预训练词向量文本变成字典形式:{word:vector} :param pretrained_wv_path: 'Tencent_AILab_ChineseEmbedding.txt' # 预训练的词向量文件 """ # 使用gensim导入预训练词向量文件,不用管第一行的数值处理 wv_from_text = KeyedVectors.load_word2vec_format(pretrained_wv_path, binary=False) embeddings_index = {} for word in wv_from_text.vocab: embeddings_index[word] = wv_from_text.word_vec(word) logging.info('Loaded {} word vectors.'.format(len(embeddings_index))) return embeddings_indexdef gen_embedding_matrix(self, word_index, embeddings_index, embed_size): """ 从庞大的预训练的词向量中,选出训练集中出现的单词的词向量,形成小型的预训练词向量 :param word_index: local dictionary :param embeddings_index: pretrained word vectors :param embed_size: 预训练的词向量的维度 :return: """ embedding_matrix = np.zeros((len(word_index) + 1, embed_size)) for word, i in word_index.items(): embedding_vector = embeddings_index.get(word) if embedding_vector is not None: # words found in embedding index will be pretrained vectors. embedding_matrix[i+1] = embedding_vector # i+1 是为了处理OOV,使得预测时未见过的词为0 else: # words not found in embedding index will be random vectors with certain mean&std. embedding_matrix[i+1] = np.random.normal(0.053, 0.3146, (1, embed_size))[0] # 0.053, 0.3146 根据统计 # save embedding matrix embed_df = pd.DataFrame(embedding_matrix) embed_df.to_csv(self.path_embedding_matrix, header=None, sep=' ') return embedding_matrix 写在最后 之前在做QA任务的优化时,尝试使用预训练的词向量,那时候还没直接用Bert。先说结果:效果提升不大,甚至说没啥提升。 主要原因:某垂直领域的词向量,太接近了,起不到分开词意的作用。词向量可能在非常泛的语义区分中有作用,比如在聊天的时候,谈的天南地北,能分清钢琴和大米是两回事,但是可能分不清钢琴和吉他,甚至两个向量表达十分地接近。 后面接触Bert之后,就没有深入再做词向量预训练的工作了,只能说Bert使人懒惰😂,接下来会写几篇Bert实战相关文章,敬请期待~ 参考:Using pre-trained word embeddings in a Keras model]]></content>
<tags>
<tag>word embeddings</tag>
</tags>
</entry>
<entry>
<title><![CDATA[如何在Hexo中添加本地图片]]></title>
<url>%2F2019%2F11%2F06%2Fadd-local-img%2F</url>
<content type="text"><![CDATA[在写文章时,经常需要插入一些本地的截图到文章中,按照经验,解决的办法自然是将图片上传到某个图片空间,然后将图片空间中图片的链接插入文章中。这当然可以解决问题,但是未免太麻烦。下面介绍两种方法: 第一种方法 在配置文件_config.yml里修改:post_asset_folder: true 在Hexo安装目录下执行:npm install hexo-asset-image --save,这是下载安装一个可以上传本地图片的插件 等待一段时间之后,再运行hexo n "文章标题"来生成博文时,/source/_post文件夹中除了文章标题.md外,还有一个同名文件夹。 在新的博文中想引入图片时,可以先把图片复制到博文的同名文件夹,然后在.md中按照常规的方式饮用图片即可,如![你想输入的替代文字](博文标题/图片名.jpg)。注意,此处的图片路径必须使用相对路径 执行hexo g,检查生成的页面中图片的src地址。此时生成页面中图片src地址应该与页面的相对路径一致(具体路径取决于页面路径格式设置) 第二种方法以上方法可以解决本地图片上传和引用的问题,但是在每个文章下建立资源文件夹好处是分类清楚,缺点是图片复用不方便,也不符合网站设计的一般规范。 所以我们可以第二种方案: 在本地source中建立img文件夹,将引用到的图片全部放在此文件夹中。这样操作也便于图片的复用。 注意,采用这种方法时无需修改_config.yml,也无需安装hexo-asset-image]]></content>
</entry>
<entry>
<title><![CDATA[Hexo接入谷歌广告 Google AdSense]]></title>
<url>%2F2019%2F11%2F06%2Fgoogle-adsense%2F</url>
<content type="text"><![CDATA[快到年底又开始折腾网站。经常在别人的网站里看到google的广告,也想通过自己的网站实现一丢丢变现(蚊子腿再小也是肉啊…嘿嘿)后来跟一个妹子提起说是叫“谷歌联盟”,于是自己查阅了相关的资料,整理了一份接入谷歌广告联盟google adsense的流程,供大家参考~ 准备环境 google adsense对网站内容有要求:首先你要有个网站,必须有文章有内容,否则会被拒 需要一个可以翻墙的环境 注册账号google adsense注册账号流程比较简单,入口在这里:Google Adsense 添加广告代码 注册账号完成之后,google会生成一段代码。 需要将谷歌提供给你的一份代码添加到你网站的中,因为我目前是next主题,因此放到\themes\next\layout_partials\head.swig 任意一个位置(本人放在了最后)即可。 1<script data-ad-client="ca-pub-2691877571661707" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> 在你添加完成之后,hexo deploy网站,点击确认,谷歌会到你的网站上进行核查和验收,一般没问题的话几分钟就会出结果,有问题的话要等待一段时间。 个性化配置广告位待通过审核之后,就可以开始考虑在自己的网站上进行广告位置的筛选和设计了,目前google adsense主要提供了自动广告和广告单元两种形式的广告添加方式。 自动广告(不推荐)自动广告是google adsense近来提供的一种广告形式,它能够通过分析你的博客布局结构,自定义的在你的网站中插入合适的广告,无论是内容,还是广告尺寸,都是完全契合网站内容本身的,算是一种比较高质量的广告。 但是根据我的使用经验,这种广告投放的几率比较小,往往好几篇文章或页面才会投放一个广告内容,效率比较低(唯一的好处是,如果你的网站支持移动端查看的话,会自动投放移动端自适应的广告) 具体的代码插入方法,其实就是上述的用来检验的代码,一旦上述代码审核通过,其实已经自动接入了google adsense的自动广告。 广告单元(推荐)为了能够最高效的利用自己博客的广告位,adsense还提供了三种固定广告位 文字广告和展示广告(即侧边栏,评论区之类的固定广告位) 信息流广告(插入在信息流内容的广告位置) 文章内嵌广告(主要是插入在每篇文章内部的开始,中间,结尾部分,展示次数比较多,强烈推荐) 由于本人的是博客网站,所以第二种信息流广告没有投入使用,这里主要使用了第三种。具体的操作流程是: 在网站上,选择广告单元->新建广告位->选择对应的广告类型->生成对应的广告代码。 在themes/next/layout/_custom下新建google_adsense.swig文件,将代码复制过去。 google_adsense.swig文件引用位置决定广告展示位置,位置自己决定。 插入评论区:将代码插入\themes\next\layout\_partials\comments.swig 中的末尾即可。12345678910<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-2691877571661707" data-ad-slot="1301633292"></ins><script> (adsbygoogle = window.adsbygoogle || []).push({});</script> hexo deploy代码到github pages上,可能需要20-30分钟广告才会显示在网页上。我的很快,基本1-2分钟就显示了。 注意事项在成功接入AdSense广告之后,并不算结束,Google会根据几种方式和数据判断广告点击是否作弊,从而注销你的账号。所以不要心存侥幸心理,好好发原创文章,提高网站的质量才是王道。 作弊广告点击者的IP地址与你Adsense账户登录IP地址相同 作弊广告点击的CTR数据太高 作弊广告点击者的IP地址来自同一个地理区域 根据Cookies判断作弊Adsense广告点击 作弊广告点击者页面停留时间太短 直接访问者的广告点击率过高 流量小但广告点击率高 在网页上用文字提示请求鼓动点击广告 务必不要点自己的google adsense广告(听闻很危险) 务必不要让公司内部的同事去点击广告(听闻也很危险) 收入攒够100美金才能提现 说在最后下面就是我的账面初始化了,只能说,都是通过各种方法来鼓励自己好好写文章,好好维护网站,初心带来快乐和成长~ 加油!]]></content>
<tags>
<tag>ads</tag>
<tag>google</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CCF-GAIR-2019 参会体验]]></title>
<url>%2F2019%2F07%2F18%2Fccf-gair-2019%2F</url>
<content type="text"><![CDATA[这是第二次去CCF-GAIR,第二年,第二次,可以看清很多东西,比如教授的ppt会重复,比如干货在什么时候产出不重要,在什么时候听什么时候传播才是发挥价值的时候;第一次听的时候会觉得接触了新世界;第二次听就是,哦,我懂。失去了初学者的乐趣。 总之这是一场大型的大佬social,渣渣打酱油的会议。好在亲眼见了两个idol,周明和杨强。 去年参会体验链接:CCF-GAIR-2018 参会体验 今年不打算像去年那样什么都写一点了,打算只写点与NLP相关的东西。但是说实话,相关会场的也不多。虽然没有专门的NLP会场,但是旁听其他会场后还是有一些NLP的影子,比如:平安的智能HR,科大讯飞的智能批作业,微众银行的智能风控,其它的还有智能舆情监控,智能客服,搜索引擎等等。我打算只写周明老师的分享会观后感。周明老师是我的爱豆~ 做NLP的应该无人不知无人不晓了吧。也不打算抄着雷锋网的笔记了,先上一篇雷锋网总结的笔录:周明:自然语言处理的未来之路 | CCF-GAIR 2019 题目:自然语言处理的未来之路 | 演讲人:周明分享的前期的NLP科普和发展史略过,周明老师对于NLP的现状和未来发展的看法:过去40年,自然语言基本上经历了从规则到统计,到现在的神经网络。相比过去,目前可以说是自然语言处理最黄金的时期,在很多领域都取得了突破性的进展。但我们审慎地看到神经网络自然语言处理过度依赖计算资源和数据,在建模、推理和解释方面还存在许多的不足。目前存在的问题: 第一个是无休止的计算资源的军备竞赛。这个比较好理解,在目前的NLP的比赛里,资源相争还是一大潮流,大家喜欢把注压在机器性能提升上。周博士原话:现在大家都用大规模的机器训练,同样的算法,只要训练速度快,就可以快速迭代,然后你的水平就比别人高。与之同时,当然也特别耗资源,许多模型一训练可能要好几天或者好几万美金。有时候它管事,但有时候也不管事。 第二个是过度依赖数据。首先你要标数据,标注的代价是非常大的。其次,数据有隐含歧视的问题,通过数据分析,可能会得到歧视性的结果。另外数据有偏差,数据在标注的时候请人标注,人都是偷懒的,想最简单的方法去标注,结果标注的数据千篇一律,基于这样的数据学的模型也只能解决标注的数据,拿到真实任务上由于跟你标注分布不一样,所以根本不好使。比如说我们做Q&A问答系统,我们在所有的问答里面都假设是第一名,但到了搜索引擎上有很多简单的问题都解决不好。此外,还有数据隐私保护等等问题。 再看目前使用神经网络处理的三种典型任务,如果解决的好,自然语言的任务就基本OK了: Rich resource 比如机器翻译任务,上下文,还未能真正做到歧义消解,人类知识的借鉴 Low resource 没什么语料的任务,学起来很难,因此要借力: transfer learning 迁移学习,NLP的新范式:预训练+细调:我们可以针对大规模的语料,提前训练好一个模型,这个模型既代表了语言的结构信息,也有可能代表了所在领域甚至常识的信息,只不过我们看不懂。加上我们未来的预定的任务,这个任务只有很小的训练样本,把通过大训练样本得到的预训练模型,做到小训练样本上,效果就得到了非常好的提升。 cross-lingual learning 跨语言学习 unsupervised 无监督 prior knowledge; human role 先验规则;字典;人的强化学习 利用种子进行迭代学习,比如我有一个小辞典,有几条规则,有几条双语,我能不能用它当做一个引子,做一个冷启动,启动之后再迭代改进。 Multi-turn task 多轮任务,例如智能客服,涉及语义分析,指代消解,省略部分补充等任务目前的情况/劣势: 缺乏常识&推理推理是要做很多事情。第一是要了解上下文,说过什么话,答过什么问题,干过什么事都要存储起来,记忆起来。第二是各种各样的知识要用起来。第三才是推理的部分,这里面涉及到语义分析、上下文的指代消解、省略消解。最后,还有就是可解释的问题,如果你的推理不可解释的话,那就没有人会相信,导致你的系统无法进行进一步的推进。 前后不一致:时间、空间、逻辑不一致 未来的方向,可解释的,有知识的,有道德的,可自我学习的NLP,从现存的实际任务出发。 周明老师总结了NLP的未来之路主要有6个角度非常重要:计算机的能力,数据,模型,人才,合作,应用。 很多周明教授提到的问题其实我之前都有遇到过,但是当时想法就是,该怎么解决,结果就是花好大力气都只提升很小甚至没有提升,后来才发现这是行业内的问题。因此应该多借鉴他人在遇到问题时的情况,当时无知以为是只有自己才遇到了这些问题,钻在里面就不出来,这样收效甚微。 写在会后 虽然NLP是研究和发展了好多年,但我还是觉得这是很年轻的技术,谈不上成熟,因此还有很多可以研究和探索的地方,难度也是可想而知的,但是莫名地对有提升空间的东西感兴趣,这样对初学者也比较友好,追赶难度小。 这种会议最好是带着问题去,不然收获不了什么。无论哪种学习形式,有人分享的场合里,首先了解分享概要和分享方向,将其与自己的知识体系做匹配,又不相关又不感兴趣的,无论人家在讲什么,听者都是浪费时间。而刚好与自己的研究方向匹配的分享会里,最好是带着自己已有的思考进去,不然错过台上的人在讲什么也是分分钟的事。 就我本身来说,很想有人告诉我在NLP领域,做什么技术或者做什么项目是政治正确学术正确的选择,然而大家都忙着发展自己的项目打自己的广告,没有人想为你的选择负责,至于自己真正具体要做什么,还是要靠自己来判断。会议上分享来分享去的东西,甚至都是动动手指谷歌百度知乎能告诉你的东西,那么亲临现场的意义到底在哪里?有一个氛围强迫自己接收信息?No idea yet. 再说穿一点就是,有什么东西是这个会议能给你的而搜索引擎不能给你的,而你在会场拿到这个东西了吗? 有时候,做新东西没有做有用的东西来的有意义。招不在新,管用就行。 Think Out of The Box下面结合自己的经历和工作内容聊聊NLP的一些落地方向和自己想做的东西。思考的方向大致从三个方向: 物联网下的NLP应用 跨领域的NLP应用 NLP Trend 1. 物联网下的NLP应用因为从事于物联网的智能家居行业,对这方面的应用还是比较感兴趣的。主要聊聊两个已经在工业界发育的比较好的项目:语音助手和智能客服 。甚至两者有很多可以交叉的地方。 语音交互 / 语音助手首先,为什么要做语音助手呢,尤其在各大厂已经出品优秀成熟的智能音箱/语音助手(小爱同学,微软小冰,Alexa,Siri等等)之后,为什么还要自己做呢?这里首先想澄清,这里提到的做语音助手是在很垂直领域的一个小功能模块。就是在智能家居小家电生态的语音交互功能,例如控制设备,预约操作,或是设备联动自动化设置等等。而不是从头开始重新做一个小爱同学,那样是以卵击石的自杀行为。 因为本身这些网关、开关、传感器等设备是公司生产的主力产品,因此在与用户最直接的语音沟通交互时,应该把真实的语音交互文本数据拿到,根据实际场景将交互做的更加智能便捷。 需要说明的几点: 垂直领域,在智能家居,语音交互应该是人机交互的主流,作为一家智能家居生态公司,语音交互是必不可少的技术积累,而这些文本数据或者是设备信息,都是其它公司很少有的。 做辅助,只做自己该做的,只做自己擅长的,嵌入式语音交互不花力气在聊天上,而是聚焦在产品使用,控制,联动等功能上。使得原先只能在app上触屏交互增加新的交互方式。 前期可能基于大量的规则约束,为了铺开交互渠道,好用有用就行。 如此以来,当使用率上升,市场扩张,可收到大量的产品升级需求等反馈时,可以增加语音助手的辅助功能,例如: 使用介绍 预约家电 控制家电 智能问答 聊天 自动化配置 智能问答 / 搜索引擎还是一样的,首先明确为什么要做这个智能客服,在其它公司可能已经有更成熟的落地技术的情况下,为什么?What’s the difference here? 首先因为物联网的科普还需要一段时间,比如智能家居的普及,还需要3-5年,而普及的过程中少不了询问产品的信息,使用的方法,能实现的场景等等。这些都是与公司强相关的业务,也利用公司产品的推广。有些事情不是别人在做了而收手,决定该不该做这件事的基础,应该是你在什么位置上,你有没有这个使命去将这件事做好。 笔记备注: 对检索query的理解 文本改写 纠错 用户搜索意图 知识图谱/知识挖掘,用于提升智能问答体验 直接做语音客服机器人 ,涉及多轮对话。 订单处理,真实场景 产品推荐 配送服务查询,产品使用科普 2. 跨领域的NLP应用跨领域的应用可能是与其它方向结合,比如与CV结合的看图说话。但是这相当于,处理二手信息,CV图像获取第一手信息,再根据图像获取的信息里处理第二手信息。暂时除了好玩没有找到特别强的应用实现理由,因为语音交互已经是很强的交互行为了。 另一个说法可以是跨部门的应用,向公司的其它部门提供平台服务: 商品搜索,商品推荐 需求管理/other 可视化 3. NLP Trend 平台搭建模块 打比赛 Fine-tune,transfer learning 暂时想到这么多,那就先写到这里吧。]]></content>
<tags>
<tag>NLP</tag>
<tag>ccf</tag>
</tags>
</entry>
<entry>
<title><![CDATA[word2vec]]></title>
<url>%2F2019%2F06%2F19%2Fword2vec%2F</url>
<content type="text"><![CDATA[等有空整理,先放灵感链接。以及,word2vec就是利用单词的环境去表达一个单词。如果某些单词常出现的环境是很像的,那么它们的向量就很接近。也不是所谓的无监督,是有target的,只不过这个target可以用一条规则形成,而无需人工标注。在我眼里,这还是有监督的训练。毕竟所有神经网络都是有监督的学习。 links 图解Word2vec Word2vec初探 知乎:word2vec是如何得到词向量的? gensim - Word2vec embeddings An Intuitive Understanding of Word Embeddings: From Count Vectors to Word2Vec]]></content>
<tags>
<tag>NLP</tag>
<tag>word2vec</tag>
</tags>
</entry>
<entry>
<title><![CDATA[论文笔记 - Attention Is All You Need(BERT原理)]]></title>
<url>%2F2019%2F06%2F19%2Fattention-is-all-you-need%2F</url>
<content type="text"><![CDATA[首先,论文链接:Attention Is All You Need 按下面的链接顺序看就可以了,应该都能看懂。等有空再整理成文章。 必看的参考链接: 最最精彩的论文解读/图解:The Illustrated Transformer 知乎的一篇Transformer笔记 哈佛nlp组用pytorch实现的Transformer代码 苏神的《Attention is All You Need》浅读(简介+代码) Attention Mechanism详细介绍:原理、分类及应用 下面介绍一下在NLP中常用attention的计算方法(里面借鉴了张俊林博士”深度学习中的注意力机制(2017版)”里的一些图)。Attention函数的本质可以被描述为一个查询(query)到一系列(键key-值value)对的映射,如下图。 在计算attention时主要分为三步,第一步是将query和每个key进行相似度计算得到权重,常用的相似度函数有点积,拼接,感知机等;然后第二步一般是使用一个softmax函数对这些权重进行归一化;最后将权重和相应的键值value进行加权求和得到最后的attention。目前在NLP研究中,key和value常常都是同一个,即key=value。]]></content>
<tags>
<tag>NLP</tag>
<tag>attention</tag>
<tag>bert</tag>
<tag>transformer</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Json In Python]]></title>
<url>%2F2019%2F04%2F26%2Fjson-in-python%2F</url>
<content type="text"><![CDATA[玩数据的时候,免不了与json接触。这两天在玩漫威的API,采集数据和存储数据时,因为我没有MongoDB数据库,免不了要将Json(dict)数据格式保存,再将其转换成python的各种数据格式:dataframe或dict等。在此记录几个场景,和解决办法: Json单条举例:多dict嵌套,模拟实际接触到的数据类型1dct = {'aaa': 'bbb', 'ccc': {'ff': 'gg', 'dd': 'ee', 'hh': {'i': 'j'}}} CASE 1:逐条获得dict数据时,(逐条)写进data1.json12345678910# 对 Marvel API请求数据时,是逐条获取dict的,此处‘dct’表示逐条获取的内容dct = {'aaa': 'bbb', 'ccc': {'ff': 'gg', 'dd': 'ee', 'hh': {'i': 'j'}}}with open(r'data1.json','w') as file: # 新建 data1.json文件 for i in [1,2,3,4]: # [1,2,3,4]这里写4条,只做示范 js = json.dumps(dct) # 将python格式的dict编码成json需要的格式 file.write(js) # 逐条写入 file.write('\n') # 逐条换行写入file.close() CASE 2:(逐条)读取data1.json里的内容12345678910# data1.json 里的逐条内容都是以 str 形式存储的# 相当于要将 str(='dict') 转换成 dictimport astff = open(r'data1.json','r')for q in ff: q = ast.literal_eval(q) print(q) # 这里的q已经是python的dict格式,可以按key取valueff.close() 这里我尝试了各种 json.load, pd.read_json都没有成功,最后用ast一步解决。 CASE 3:逐条获得dict数据时,(逐条)对dict进行变换再写进data2.json12345678910# 有时需要对获取的dict数据再作处理再保存dct = {'aaa': 'bbb', 'ccc': {'ff': 'gg', 'dd': 'ee', 'hh': {'i': 'j'}}}with open(r'data2.json','w') as file: # 新建 data2.json文件 for i in [1,2,3,4]: # [1,2,3,4]这里写4条,只做示范 js = json.dumps({str(i): dct}) file.write(js) # 逐条写入 file.write('\n') # 逐条换行写入file.close() CASE 4:对data2.json的json文件进行内容读取12345678# 此时用ast却报错了,因此换 yamlimport yamlff = open(r'data2.json', 'r')for q2 in ff: q2 = yaml.load(q2) print(q2) # 这里的q2已经是python的dict格式,可以按key取valueff.close()]]></content>
<tags>
<tag>python</tag>
<tag>json</tag>
</tags>
</entry>
<entry>
<title><![CDATA[漫威英雄图谱 - gephi]]></title>
<url>%2F2019%2F04%2F25%2Fgephi%2F</url>
<content type="text"><![CDATA[在Github建了一个repo,主要用来玩 Marvel漫威 的数据 [github] 。原意是想用这些做个知识图谱的小demo,顺便做做图谱可视化。昨天看到一个基于gephi的漫威角色图谱的教程[tutorial] ,主要教你怎么画出下面这张图,想学的可以继续看下去: 画图大概需要哪些东西: gephi 所需的数据 使用 gephi 生成英雄关系图谱 获取数据 方法1 - 现成的data:inode.csv ,iedge.csv 方法2 - 自己获取:marvel_gephi.py原po是把获取的数据存储在MongoDB,有数据库的可以参考教程原码 [tutorial] 方法3 - 自己获取其他数据 相关参数解释: 节点(node)也就是顶点(vertical)。两个节点相连的部分称为边(edge)。 inode文件中:id 为英雄角色id ,即图中的节点,name 为英雄角色名字,id 与 name 一一对应;weight 为每一个英雄相关的所有故事数量,即节点的大小(权重)。 iedge 文件中:source 与 target 为源 id 与目标 id,表示两个角色之间的联系,即图中的边,本测试中因选择无向边,所以 source 与 target 不表示方向边表示边(无向);weight 为同一故事中出现这两个英雄的故事数量,即图中边的大小(权重)。 Gephi 使用gephi就是一个画图工具,画这种关系图谱特别好看。下载会遇到些环境问题,个人感觉靠运气吧,实在不行就换个环境跑。 本次使用输入 gephi 文档:inode.csv, iedge.csv 步骤: 从【文件】—>【打开文件】中导入节点文件与边文件,注意导入边表格时,选择添加到【已存在的工作空间】(Append to existing workspace) 。 选择【布局】—>【选择一个布局】—>【Fruchterman Reingold】 ,进行相关算法运算。 选择【统计】—>【运行模块化】,计算 modularity_class 数据 外观4.1 选择【外观】—>【节点】+【颜色】—>【Partition】—>【Modularity Class】:利用刚刚的模块化数据进行分类。4.2 选择【外观】—>【节点】+【大小】—>【Ranking】—>【Weight】: 使用传入的 weight(故事数)决定节点大小。4.3 选择【数据资料】—>【数据表格】—>【复制数据到其它列】:将 name 列复制到 Label 列。4.4 选择【图】—>【T】即可显示标签。4.5 在【预览】处自己调整好需要的数据后,点击【SVG/PDF/PNG】,即可导出图形。 4.6 还有更多的【过滤】,【统计】,【预览】的相关功能,就由大家慢慢探索了。 图片参考及部分 gephi 设置。 节点数据: 边数据: 外观设置当在【统计】处 点击【运行模块化后】,此处出现【Modularity Class】: 【layout】(布局)设置,布局算法选择:【Fruchterman Reingold】: 【统计】部分-点击【运行模块化】: 效果图]]></content>
<tags>
<tag>gephi</tag>
<tag>marvel</tag>
<tag>data visualization</tag>
</tags>
</entry>
<entry>
<title><![CDATA[知识图谱]]></title>
<url>%2F2019%2F04%2F23%2Fknowledge-graph-intro%2F</url>
<content type="text"><![CDATA[挖个坑先。最近对知识图谱有个入门级的了解,希望以后还会有项目继续探究。年初实践了下怎么搭建一个最基本的知识图谱,顺便尝试了下使用Neo4j 搭建图数据库,在此稍作记录。入门选手可以看这篇科普帖:知识图谱的技术与应用 介绍知识图谱到底是个什么东西?说到底就是拥有一定知识,可以进行相关知识问答的系统,这个系统可以不停地输入知识,也可以输出知识。其实,每个人都是一个行走的知识图谱。我们从小就被问“几岁啦?”,“叫什么名字呀?”,当我们进行回答时就是知识输出的过程。 本质上知识图谱的建立有如下几个过程:知识存储、知识融合、知识验证、知识计算。 知识存储: 信息获取及存储的过程,比如3岁的时候学会背唐诗,8岁的时候学会加减乘除,15岁开始学物理,可以源源不断地获取不同的知识,语文、数学、英文等等。 知识融合: 很多知识都是相互关联的,比如方程求解时会用到四则运算,求微分积分也会用到四则运算,很多知识都需要不断整合。 知识验证: 对知识的正确性、实时性、整个体系的逻辑一致性的检验。比如2018年对于“小朋友你今年几岁啦?”的回答是3岁,那么到2019年这个回答的知识要更新为4岁了。 知识计算: 一个知识体系是很庞大的,有很多很多的关系。比如“小朋友今年几岁了?”,需要对语义进行计算,明白问的问题是年龄相关,而且是当前年龄,需要计算当前时间与出生时间的差值。计算问题要的是什么样的答案,而不是问年龄要数字时返回一个“天空的颜色”的答案。 先分享下两个有趣的可视化图谱: 漫威英雄的知识图谱可视化 [demo link] 星际战争知识图谱可视化 [demo link] 搭建图数据库虽然这两demo很大程度是前端写的好,对于我这种前端白痴,市面上已经有了相似的很好上手的开源工具(图数据库Neo4j),多用于金融的反欺诈,社交关系图谱等等。接下来我也尝试搭建一个基础的知识图谱。 需要思考几个基本问题: 哪里来的数据? 选择哪种数据库?数据存储格式 如何将数据导入数据库? 如何做到知识融合?进行数据库的增删改查? 如何做知识计算? 数据来源介绍一个基于中文的知识图谱开源网站:开放的中文知识图谱 openkg.cn 这个网站里有图谱数据,图谱工具,资源涵盖:常识、金融、农业、社交、物联网、气象、生活、出行、科教、医疗等等。总之是比较全面的中文知识图谱开源库了。比如我用过的是四大名著里的西游记人物关系数据:中国四大名著人物关系知识图谱和OWL本体 ,在数据与资源那边可自行下载。然后解压即可,就有csv格式的人物关系表格。 顺便介绍个openkg里的一个比较成型的知识图谱工具:思知(OwnThink) ,体验入口:思知机器人 数据库选择虽然我一开始已经选定了图数据库 ,这里还是将各种 数据结构 和 数据库 做个简单的对比: 数据类型可能是: 多元 结构化表格: 就像excel表格一样整整齐齐 JSON 键值 : key-value 组 RDF 三元组: 三元组就是两个实体,中间有一个关系。比如“中国的首都是北京”,两个实体分别是“中国”和“北京”,它们之间的关系是“的首都”,这两个实体加一个关系就是三元组。 不同的数据库: MySQL: 适合结构化数据,像excel表格 MongoDB: 适合json格式数据 Neo4j: 图数据库,可视化,适合三元组,并且实体和关系可以包含属性。 因此,选择什么样的数据库,是基于你的数据打算以什么样的格式存储。选择什么样的数据库以及怎么设计 schema。选关系数据库还是NoSQL 数据库?要不要用内存数据库?要不要用图数据库?这些都需要根据数据场景慎重选择。西游记的人物关系数据是三元组合格,这里我们选择尝试用Neo4j 图数据库。 图数据库:Neo4jNeo4j 是一个图数据库,主要包括节点和关系。节点和关系都可以包含属性。介绍文档 [doc], 社区[community]。 安装社区版下载链接:neo4j-community-3.5.2 解压后在bin目录下运行命令:neo4j console 12345678D:\>cd D:\neo4j-community-3.5.2\binD:\neo4j-community-3.5.2\bin>neo4j console2019-04-23 12:23:39.692+0000 INFO ======== Neo4j 3.5.2 ========2019-04-23 12:23:39.708+0000 INFO Starting...2019-04-23 12:23:42.950+0000 INFO Bolt enabled on 127.0.0.1:7687.2019-04-23 12:23:44.544+0000 INFO Started.2019-04-23 12:23:45.696+0000 INFO Remote interface available at http://localhost:7474/ 浏览器Chrome访问:localhost:7474,第一次访问会提示修改密码。 第一次玩可以参考这个入门小教程:Neo4j 简单入门更详细的安装以及其他信息:neo4j 安装 Cypher 语法总结Cypher入门:tutorial cypher就像MySQL的sql语言,demo示例:neo4j_movie_graph.cypher 。cypher可对数据库做增删改查。更多的操作参考:Cypher Basic ,可以自己跑几行命令找找感觉。 Py2neo [doc]毕竟写sql或者写cypher不是我擅长的事,所幸可以通过Python API py2neo 来访问neo4j。 py2neo使用教程-1py2neo使用教程-2py2neo使用教程-3 贴一些使用过的代码,要配合合适的数据集一起使用: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141import pandas as pdimport numpy as npimport itertoolsfrom py2neo import Graph,Node,Relationship,NodeMatcher# 终端运行 neo4j console之后就可以从py2neo连接neo4j数据库graph = Graph("http://localhost:7474",username="neo4j",password="neo4j")matcher = NodeMatcher(graph)# graph.delete_all() # 清空本地数据库的命令,慎用# ============================================================# Demo1: Product# 目的:把能展示的应用都展示出来prod = pd.read_excel('type.xlsx')list(prod)# 设置基础信息for i in np.unique(prod['分组类型']): node_a = Node('Type', name=i) df = prod[prod['分组类型'] == i] _p = np.unique(df['产品类型']) for j in _p: node_b = Node('Product',name=j) rela = Relationship(node_a,'includes',node_b) graph.create(rela) # 因为点Node需要定义性质,所以建议 key-value 和 rela-node分开建表# add property/relationship时需要第一列的产品已存在,否则需要更多信息# ------------------------------------------------------------# add propertyprop = pd.read_excel('data/add_property.xlsx')for _prod in np.unique(prop['product']): df = prop[prop['product'] == _prod].reset_index(drop=True) # 如果库内没有该产品,进行Node建立 if matcher.match(name=_prod).first() is None: node_target = Node('Product', name = _prod) # 'Product'是固定的 else: node_target = matcher.match(name=_prod).first() # 'Product'是固定的 for idx in range(len(df)): node_target[df['key'][idx]] = df['value'][idx] graph.push(node_target) # add relationshiprel = pd.read_excel('add_relationship.xlsx')# 检查是否存在节点 !!!希望后期的制表规则完善后可以省去检查这一步# 如果不存在,就创建# head 与 tail 如何区分#nodes = list(itertools.chain.from_iterable([np.unique(rel['product']), np.unique(rel['node_name'])]))for ind in range(len(rel)): # 如果不想重复遍历,就建chunk df if matcher.match(name=rel['node_head'][ind]).first() is None: node_head = Node(rel['head_type'][ind], name = rel['node_head'][ind]) else: node_head = matcher.match(name=rel['node_head'][ind]).first() node_tail = matcher.match(name=rel['node_tail'][ind]).first() r = rel['rela'][ind] # 检查关系是否存在,若不存在,建立关系 cmd = "MATCH a=()-[:%s]->( {name: '%s'}) RETURN a" %(r,rel['node_tail'][ind]) if len(graph.run(cmd).data()) == 0: relatp = Relationship(node_head,r,node_tail) graph.create(relatp) # ==============================================================# Demo2: Roomroom = pd.read_excel('room.xlsx')for i in np.unique(room['owner']): node_a = Node('Owner',name=i) df = room[room['owner'] == i] _r = np.unique(df['room_name']) for j in _r: node_b = Node('Room',name=j) rela = Relationship(node_a,'lives',node_b) graph.create(rela) _df = df[df['room_name']==j] _p = np.unique(_df['product_id']) for k in _p: node_c = Node('Prod',name=k) relatp = Relationship(node_b,'has',node_c) graph.create(relatp) for i in np.unique(room['product_id']): # 这些点已经存在,只需要匹配 node_a = matcher.match('Prod',name=i).first() df = room[room['product_id'] == i] _c = np.unique(df['control']) for j in _c: node_b = Node('Ctrl',name=j) # 把node的答案作为属性填充上去,然后,方便用作cypher查询 rela = Relationship(node_a,'controls',node_b) graph.create(rela)# ==============================================================# Demo3: 西游记xyj = pd.read_csv('./西游记/triples.csv')#所有人物创建完,直接搜索建关系n = [np.unique(xyj['head']),np.unique(xyj['tail'])]for i in n: for p in i: node = Node('Person', name=p) graph.create(node)for idx in range(len(xyj)): print(idx) node_a = matcher.match('Person',name=xyj['tail'][idx]).first() node_b = matcher.match('Person',name=xyj['head'][idx]).first() relatp = Relationship(node_a,xyj['label'][idx],node_b) graph.create(relatp) # 查询 '孙悟空的师傅是谁'len(graph.nodes)len(graph.nodes.match("Person"))# 'PERSON'根据命名实体匹配test = matcher.match('Person',name='唐僧').first()for rel in matcher.match(start_node=test, rel_type="徒弟"): print(rel.end_node()["name"]) 拓展大批量数据导入参考:csv文件导入Neo4j(包括结点和关系的导入)neo4j批量导入neo4j-import如何将大规模数据导入Neo4j 删空属性一般节点和关系可以通过py2neo删空,但是属性会存留:Neo4j - How to delete unused property keys from browser? 别人开发的有趣的图谱成果 [refer] 一家做NLP的公司:wisers AI lab zhishi.me Acemap 交大 贼有意思的网站以及随便点开的链接:acemap.info main page 2018 NeurIPS 2018 https://www.acemap.info/topic?topicID=0304C748 https://www.acemap.info/paper-map?topicID=0304C748 有趣的算法介绍:https://www.acemap.info/acenap/algorithms CN-DBpedia 复旦大学,我现在点开属于网站维护中,所以这块以后再补充样例数据文件是txt格式,每行一条数据,每条数据是一个(实体名称,属性名称,属性值)的三元组,中间用tab分隔,具体如下所示。【复旦大学 简称 复旦】包含900万+的百科实体以及6700万+的三元组关系。其中mention2entity信息110万+,摘要信息400万+,标签信息1980万+,infobox信息4100万+。复旦大学还有个knowledge works http://kw.fudan.edu.cn/ 可以下载数据集的网站:grouplens 一个中草药的知识服务系统:http://zcy.ckcest.cn/tcm/ 逆天http://zcy.ckcest.cn/tcm/qaos/profilenet NLPIR:http://ictclas.nlpir.org/nlpir/]]></content>
<tags>
<tag>knowledge graph</tag>
</tags>
</entry>
<entry>
<title><![CDATA[convert-jupyter-to-other-formats]]></title>
<url>%2F2019%2F04%2F23%2Fconvert-jupyter-to-other-formats%2F</url>
<content type="text"><![CDATA[自从在Github上用Jupyter notebook写笔记和教程越用越顺手后,开始对Jupyter有了越来越多的期待。本篇介绍如何将Jupyter文件(.ipynb)转换成下面的格式: html latex markdown slides rst python 在终端对应的文件目录下,命令行键入: 1$ ipython nbconvert --to FORMAT notebook.ipynb 这里notebook.ipynb 就是待转换格式的jupyter notebook文件,--to FORMAT 代表转换后的格式,可以有以下选择: --to html --template full (default) A full static HTML render of the notebook. This looks very similar to the interactive view. --template basic Simplified HTML, useful for embedding in webpages, blogs, etc. This excludes HTML headers. --to latex Latex export. This generates NOTEBOOK_NAME.tex file, ready for export. You can automatically run latex on it to generate a PDF by adding --post PDF. --template article (default) Latex article, derived from Sphinx’s howto template. --template book Latex book, derived from Sphinx’s manual template. --template basic Very basic latex output - mainly meant as a starting point for custom templates. --to slides This generates a Reveal.js HTML slideshow. It must be served by an HTTP server. The easiest way to get this is to add --postserve on the command-line. --to markdown Simple markdown output. Markdown cells are unaffected, and code cells are placed in triple-backtick (```) blocks. --to rst Basic reStructuredText output. Useful as a starting point for embedding notebooks in Sphinx docs. --to python Convert a notebook to an executable Python script. This is the simplest way to get a Python script out of a notebook. If there were any magics in the notebook, this may only be executable from an IPython session. 示例如果想将Jupyter文件(test.ipynb)转换成markdown格式,运行终端,在该文件的目录下,键入命令,就会生成新markdown文件: 1$ ipython nbconvert --to markdown test.ipynb 参考:Converting notebooks to other formats]]></content>
<tags>
<tag>jupyter</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Keras分词器 Tokenizer]]></title>
<url>%2F2019%2F04%2F23%2Fkeras-tokenizer%2F</url>
<content type="text"><![CDATA[介绍一下keras的分词器Tokenizer,以及我在上面吃过的亏。 TokenizerTokenizer是一个将文本向量化,转换成序列的类。用来文本处理的分词、嵌入 。 1234567keras.preprocessing.text.Tokenizer(num_words=None, filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n', lower=True, split=' ', char_level=False, oov_token=None, document_count=0) 参数说明: num_words: 默认是None处理所有字词,但是如果设置成一个整数,那么最后返回的是最常见的、出现频率最高的num_words个字词。一共保留 num_words-1 个词。 filters: 过滤一些特殊字符,默认上文的写法就可以了。 lower: 是否全部转为小写。 split: 分词的分隔符字符串,默认为空格。因为英文分词分隔符就是空格。 char_level: 分字。 oov_token: if given, it will be added to word_index and used to replace out-of-vocabulary words during text_to_sequence calls 相关的类方法 方法 参数 返回值 fit_on_texts(texts) texts:要用以训练的文本列表 - texts_to_sequences(texts) texts:待转为序列的文本列表 序列的列表,列表中每个序列对应于一段输入文本 texts_to_sequences_generator(texts) texts:待转为序列的文本列表 本函数是texts_to_sequences的生成器函数版,返回每次调用返回对应于一段输入文本的序列 texts_to_matrix(texts, mode) texts:待向量化的文本列表;mode:‘binary’,‘count’,‘tfidf’,‘freq’之一,默认为‘binary’ 形如(len(texts), nb_words)的numpy array fit_on_sequences(sequences) sequences:要用以训练的序列列表 - sequences_to_matrix(sequences) sequences:待向量化的序列列表; mode:同上 返回值:形如(len(sequences), nb_words)的numpy array 属性 word_counts: 字典,将单词(字符串)映射为它们在训练期间出现的次数。仅在调用fit_on_texts之后设置。 word_docs: 字典,将单词(字符串)映射为它们在训练期间所出现的文档或文本的数量。仅在调用fit_on_texts之后设置。 word_index: 字典,将单词(字符串)映射为它们的排名或者索引。仅在调用fit_on_texts之后设置。 document_count: 整数。分词器被训练的文档(文本或者序列)数量。仅在调用fit_on_texts或fit_on_sequences之后设置 123456789101112131415161718192021222324252627282930313233from keras.preprocessing.text import Tokenizer# Using TensorFlow backend.# 创建分词器 Tokenizer 对象tokenizer = Tokenizer()# texttext = ["今天 北京 下 雨 了", "我 今天 加班"]# fit_on_texts 方法tokenizer.fit_on_texts(text)# word_counts属性tokenizer.word_counts# OrderedDict([('今天', 2),# ('北京', 1),# ('下', 1),# ('雨', 1),# ('了', 2),# ('我', 1),# ('加班', 1)])# word_docs属性tokenizer.word_docs# defaultdict(int, {'下': 1, '北京': 1, '今天': 2, '雨': 1, '了': 2, '我': 1, '加班': 1})# word_index属性tokenizer.word_index# {'今天': 1, '了': 2, '北京': 3, '下': 4, '雨': 5, '我': 6, '加班': 7}# document_count属性tokenizer.document_count# 2 需要注意的点是,由于书写习惯,英文文本的单词之间是用空格隔开的,split=' ' 这个参数可以直接对英文文本进行空格分词。但是对中文不行,因此使用 tokenizer.fit_on_texts(text) 时,text如果是英文文本,可以直接 text = ["Today is raining.", "I feel tired today."] ,但是text是中文文本的话,需要先将中文文本分词再作为输入text: text = ["今天 北京 下 雨 了", "我 今天 加班"] 这里就是我踩过的坑了,之前拷代码下来跑的时候,别人用的是英文文本,没问题,但是我的输入是中文文本,导致分词步骤利用空格对中文分词,会将整句话当作一个token,而且是字典里找不到的token,这样会造成大量的相同的嵌入表达和相同的预测分数。 因此,keras的Tokenizer对于英文文档可以做分词+嵌入 两步,对于中文的话,其实只有嵌入这步。 嵌入示例123456789101112131415161718192021222324from keras.preprocessing.text import Tokenizerfrom keras.preprocessing.sequence import pad_sequences# 1. 创建分词器 Tokenizer 对象tokenizer = Tokenizer() # 里面的参数可以自己根据实际情况更改# 2. 整理整体语料,中文需空格分词text = ["今天 北京 下 雨 了", "我 今天 加班"]# 3. 将Tokenizer拟合语料,生成字典,形成新的tokenizertokenizer.fit_on_texts(text)# 4. 保存tokenizer,避免重复对同一语料进行拟合import joblibjoblib.dump(tokenizer, save_path)# 5. 整合需要做嵌入的文本,中文需要空格分词new_text = ["今天 回家 吃饭", "我 今天 生病 了"]# 6. 将文本向量化list_tokenized = tokenizer.text_to_sequence(new_text)# 7. 生成训练数据的序列X_train = pad_sequences(list_tokenized, maxlen=200) 写在最后当遇到一个不错的API,不妨把参数看全,免得栽在一个小参数上。]]></content>
<tags>
<tag>keras</tag>
<tag>tokenizer</tag>
</tags>
</entry>
<entry>
<title><![CDATA[解决GitHub commit不显示绿色的问题]]></title>
<url>%2F2019%2F04%2F10%2Fgithub-commit-not-green%2F</url>
<content type="text"><![CDATA[之前好不容易下决心立个flag:a commit a day, green wall repay. 是不是朗朗上口,但是自己明明commit了很多次,GitHub墙怎么也不绿。 Contributions未被Github计入的几个常见原因 进行Commits的用户没有被关联到你的Github帐号中。 不是在这个版本库的默认分支进行的Commit。 这个仓库是一个Fork仓库,而不是独立仓库。 我的是独立仓库,查了一下发现我的git记录指向另一个账号,原来是commit的邮箱和用户名不对。可以用git show 发现邮箱那里跟github的账号邮箱不一样。解决步骤: 使用git show 查看本地端的邮箱 在GitHub的个人账号的设置里,找到Email,找到 Add email address,把本地邮箱填上去 添加并绑定验证,刷新,绿色格子就出来了! 当然也可以修改本地git配置 git config —global user.name “username”git config —global user.email “[email protected]”]]></content>
</entry>
<entry>
<title><![CDATA[Kaggle - Bag of Words Meets Bags of Popcorn]]></title>
<url>%2F2019%2F03%2F12%2Fkaggle-movie-reviews%2F</url>
<content type="text"><![CDATA[说起sentiment analysis,就不得不说起NLP选手必做题:Bag of Words Meets Bags of Popcorn ,以下简称“影评分析题”。必须负责任的说,这是一道很简单的题,就是对一段影评进行情感倾向预测(positive/negative)。数据为英文文本,数据集自己下载:🔗data 文本清洗技巧1. re:正则表达式 Regular Expression中文处理:除了中文,其他字符全部去掉。这样处理后的output只剩下中文和连接断句的逗号。这个是把非中文的字符比如数字、英文、标点符号、html文本等全部洗掉了,比较狠。这个技能在中文数据集上会比较有用。 123456789101112131415161718import redef only_chinese(comment): line = comment.strip() # 去除首尾空格 p2 = re.compile(u'[^\u4e00-\u9fa5]') # 中文的编码范围是:\u4e00到\u9fa5 zh = " ".join(p2.split(line)).strip() outStr = ",".join(zh.split()) # 所有的断句全部用逗号连接 return outStrcomment = " 武林外传的情节设计基本没什么bug!╭(●`∀´●)╯!!\ 看了10年都看不腻~送你个网pan链接:\ http://fakewebsite.com"test = only_chinese(comment)print(test)# output# 武林外传的情节设计基本没什么,看了,年都看不腻,送你个网,链接 英文处理:除了英文字母,其他字符全部替换为空格。正则表达式就是专治各种不服。 123456import recomment = '最喜欢的话是Coding is the new SEXY!'review_text = re.sub("[^a-zA-Z]"," ",comment)print(review_text)# Coding is the new SEXY 2. BeautifulSoup:清洗HTML、垃圾符号很多网上爬下来的评价内容会带有HTML类型的文本,都是无效信息需要删除,首先”pip install beautifulsoup4“ 1234567from bs4 import BeautifulSoupreview = '<br /><br />\"Elvira, Mistress of the Dark\"'review_text = BeautifulSoup(review).get_text()print(review_text)# "Elvira, Mistress of the Dark" 这样洗出来的文本”Elvira, Mistress of the Dark”就是真正有效的信息了。 了解文本清洗技巧之后,回到影评情感分析题本身,接下来开始正文。 第一步:创建文本清洗函数影评分析题的文本是英文的,首先要创建一个简单的函数,将评论清理成我们可以使用的格式。我们只想要原始文本,而不是其他相关的HTML,或其他垃圾符号。 12345678910111213141516171819import refrom bs4 import BeautifulSoup def review_to_wordlist(review): ''' Meant for converting each of the IMDB reviews into a list of words. ''' # First remove the HTML. review_text = BeautifulSoup(review).get_text() # Use regular expressions to only include words. review_text = re.sub("[^a-zA-Z]"," ", review_text) # Convert words to lower case and split them into separate words. words = review_text.lower().split() # Return a list of words # return(words) return(" ".join(words)) 加载数据,按照上面的函数将样本数据进行清洗: 1234567891011import pandas as pd# load datadata = pd.read_csv('data/labeledTrainData.tsv',delimiter="\t")# clean dataclean_data = []for rv in data['review']: clean_data.append(review_to_wordlist(rv)) data['clean_review'] = clean_data 我这里把整个labeled train data做为整个数据集,将其拆分成训练集、测试集: 1234from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(\ data['clean_review'],data['sentiment'], test_size=0.2, random_state=1) 第二步:生成词向量先看一下影评的平均文本长度: 12data['clean_review'].apply(lambda x: len(x.split(" "))).mean()# 236.82856 使用keras的Tokenizer进行分词: 12345678910from keras.preprocessing.text import Tokenizerfrom keras.preprocessing.sequence import pad_sequencesmax_features = 6000 # 字典最大数tokenizer = Tokenizer(num_words=max_features)tokenizer.fit_on_texts(X_train)list_tokenized_train = tokenizer.texts_to_sequences(X_train)maxlen = 130 # 句子最大长度X_tr = pad_sequences(list_tokenized_train, maxlen=maxlen) 第三步: 创建 分类器/模型天底下的分类器千千万,可以随自己选几个尝试一下。 BiLSTM Classifier 12345678910111213141516171819from keras.layers import Dense , Input , LSTM , Embedding, Dropout , Activation, GRU, Flattenfrom keras.layers import Bidirectional, GlobalMaxPool1Dfrom keras.models import Model, Sequentialfrom keras.layers import Convolution1Dfrom keras import initializers, regularizers, constraints, optimizers, layersembed_size = 256model = Sequential()model.add(Embedding(max_features, embed_size))model.add(Bidirectional(LSTM(32, return_sequences = True)))model.add(GlobalMaxPool1D())model.add(Dense(20, activation="relu"))model.add(Dropout(0.05))model.add(Dense(1, activation="sigmoid"))model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])batch_size = 100epochs = 5model.fit(X_tr,y_train, batch_size=batch_size, epochs=epochs, validation_split=0.2) 需要一点时间,运行时会出现: 123456789101112Train on 16000 samples, validate on 4000 samplesEpoch 1/516000/16000 [==============================] - 31s 2ms/step - loss: 0.4832 - acc: 0.7597 - val_loss: 0.3214 - val_acc: 0.8608Epoch 2/516000/16000 [==============================] - 30s 2ms/step - loss: 0.2633 - acc: 0.8929 - val_loss: 0.3142 - val_acc: 0.8642Epoch 3/516000/16000 [==============================] - 30s 2ms/step - loss: 0.1876 - acc: 0.9292 - val_loss: 0.3474 - val_acc: 0.8557Epoch 4/516000/16000 [==============================] - 29s 2ms/step - loss: 0.1211 - acc: 0.9593 - val_loss: 0.4179 - val_acc: 0.8560Epoch 5/516000/16000 [==============================] - 29s 2ms/step - loss: 0.0760 - acc: 0.9754 - val_loss: 0.5393 - val_acc: 0.8440<keras.callbacks.History at 0x2684c124940> 第四步: 模型评估123456789list_sentences_test = X_testlist_tokenized_test = tokenizer.texts_to_sequences(list_sentences_test)X_te = pad_sequences(list_tokenized_test, maxlen=maxlen)prediction = model.predict(X_te)y_pred = (prediction > 0.5)from sklearn.metrics import f1_score, confusion_matrixprint('F1-score: {0}'.format(f1_score(y_pred, y_test)))print('Confusion matrix:')confusion_matrix(y_pred, y_test) 评估结果: 1234F1-score: 0.8357478065700876Confusion matrix:array([[2147, 449], [ 356, 2048]], dtype=int64) 整个流程就差不多算完成啦,接下来就是进行模型优化,或者更换其它分类器。 写在最后这篇就是简单走了个流程,仅作示例。除了本文提到的技巧,还有很多细节可以填充,比如去掉停用词等;还有细节可以优化,比如调整嵌入维度等。之后有空还会继续维护本篇,填充更多有效内容。 更多参考: Random Forest Classifier Natural Language Processing in a Kaggle Competition: Movie Reviews https://nbviewer.jupyter.org/github/jmsteinw/ https://www.kaggle.com/jatinmittal0001/word2vec Kaggle - Bag of Words Meets Bags of Popcorn]]></content>
<tags>
<tag>kaggle</tag>
<tag>NLP</tag>
<tag>sentiment analysis</tag>
</tags>
</entry>
<entry>
<title><![CDATA[POS tagging 词性标注 之 武林外传版]]></title>
<url>%2F2019%2F03%2F12%2Fpos%2F</url>
<content type="text"><![CDATA[词类,POS(Part Of Speech),就是所谓的名词、动词、形容词、代词、介词等词性分类。词性标注(POS tagging)就是标记/判断一个词属于什么词性。词类有以下特征: 分布特征(Distributional)单词能够出现在相似的环境中单词有相似的功能 形态特征(Morphological)单词有相同的前缀后缀(词缀具有相似的功能)在句法结构中有相似的上下文环境 无关于含义(meaning),也无关于语法(可以是主语/宾语,等等) 词的属性可以提供很多信息: 形容词 后面跟的往往是一个名词;一句话里 名词 通常是比较重要的信息,而 介词 可能比较不重要。比如 “同福客栈的掌柜是谁?” 这句话里,重要的词有 名词:“客栈”、“掌柜” 疑问词:“谁” 词类标注是歧义消解(disambiguation) 的一个重要方面。很多次不仅仅有一个词性,当不同词性时代表的意思不同。比如“排山倒海”,原先是一个形容词,用来形容声势浩大。但是如果是出现在郭芙蓉的嘴里,那基本表示一个招式名称,是一个名词。 中文 POS tagging 体验链接:百度 、腾讯 Python Packagejieba 🔗jieba是优秀的中文分词工具,同样也有词性标注的功能。首先请确保”pip install jieba”,来个单条query: 1234import jieba.posseg as psegwords = pseg.cut("佟掌柜喜欢的人是谁?")for word, flag in words: print(word, flag) output: 12345678佟 nr掌柜 n喜欢 v的 uj人 n是 v谁 r? w 可以根据下面的jieba的词性对照表(会有变动)对上面的output进行解读: jieba词性对照表 编码 名称 注解 ag 形语素 形容词性语素。形容词代码为 a,语素代码g前面置以A。 a 形容词 取英语形容词 adjective的第1个字母。 ad 副形词 直接作状语的形容词。形容词代码 a和副词代码d并在一起。 an 名形词 具有名词功能的形容词。形容词代码 a和名词代码n并在一起。 b 区别词 取汉字“别”的声母。 c 连词 取英语连词 conjunction的第1个字母。 dg 副语素 副词性语素。副词代码为 d,语素代码g前面置以D。 d 副词 取 adverb的第2个字母,因其第1个字母已用于形容词。 e 叹词 取英语叹词 exclamation的第1个字母。 f 方位词 取汉字“方” g 语素 绝大多数语素都能作为合成词的“词根”,取汉字“根”的声母。 h 前接成分 取英语 head的第1个字母。 i 成语 取英语成语 idiom的第1个字母。 j 简称略语 取汉字“简”的声母。 k 后接成分 l 习用语 习用语尚未成为成语,有点“临时性”,取“临”的声母。 m 数词 取英语 numeral的第3个字母,n,u已有他用。 ng 名语素 名词性语素。名词代码为 n,语素代码g前面置以N。 n 名词 取英语名词 noun的第1个字母。 nr 人名 名词代码 n和“人(ren)”的声母并在一起。 ns 地名 名词代码 n和处所词代码s并在一起。 nt 机构团体 “团”的声母为 t,名词代码n和t并在一起。 nz 其他专名 “专”的声母的第 1个字母为z,名词代码n和z并在一起。 o 拟声词 取英语拟声词 onomatopoeia的第1个字母。 p 介词 取英语介词 prepositional的第1个字母。 q 量词 取英语 quantity的第1个字母。 r 代词 取英语代词 pronoun的第2个字母,因p已用于介词。 s 处所词 取英语 space的第1个字母。 tg 时语素 时间词性语素。时间词代码为 t,在语素的代码g前面置以T。 t 时间词 取英语 time的第1个字母。 u 助词 取英语助词 auxiliary vg 动语素 动词性语素。动词代码为 v。在语素的代码g前面置以V。 v 动词 取英语动词 verb的第一个字母。 vd 副动词 直接作状语的动词。动词和副词的代码并在一起。 vn 名动词 指具有名词功能的动词。动词和名词的代码并在一起。 w 标点符号 x 非语素字 非语素字只是一个符号,字母 x通常用于代表未知数、符号。 y 语气词 取汉字“语”的声母。 z 状态词 取汉字“状”的声母的前一个字母。 un 未知词 不可识别词及用户自定义词组。取英文Unknown首两个字母。(非北大标准,CSW分词中定义) 再来个多条query的例子: 12345678query = ['秀才最喜欢说的话是什么','老白的外号是什么','郭芙蓉的情敌是谁']for q in query: d = {} words = pseg.cut(q) for word, flag in words: d[word] = flag print(d) output: 123{'喜欢': 'v', '说': 'v', '是': 'v', '的话': 'u', '最': 'd', '秀才': 'n', '什么': 'r'}{'的': 'uj', '是': 'v', '老白': 'nr', '什么': 'r', '外号': 'n'}{'是': 'v', '郭': 'nr', '芙蓉': 'n', '的': 'uj', '情敌': 'n', '谁': 'r'} 基本上还是挺准的。 HanLP 🔗HanLP实际上是Java写的,pyhanlp才是python接口,因此下载是”pip install pyhanlp” 12345from pyhanlp import *print(HanLP.segment('老白的真实身份是什么'))for term in HanLP.segment('老白的真实身份是什么'): print('{}\t{}'.format(term.word, term.nature)) # 获取单词与词性 output: 1234567[老白/nz, 的/ude1, 真实/a, 身份/n, 是/vshi, 什么/ry]老白 nz的 ude1真实 a身份 n是 vshi什么 ry HanLP的词性对照表 编码 名称 编码 名称 编码 名称 P 1 =========== === ========================== ===== ============== a 形容词 gc 化学相关词汇 nf 食品,比如“薯片” ad 副形词 gg 地理地质相关词汇 ng 名词性语素 ag 形容词性语素 gi 计算机相关词汇 nh 医药疾病等健康相关名词 al 形容词性惯用语 gm 数学相关词汇 nhd 疾病 an 名形词 gp 物理相关词汇 nhm 药品 b 区别词 h 前缀 ni 机构相关(不是独立机构名) bg 区别语素 i 成语 nic 下属机构 bl 区别词性惯用语 j 简称略语 nis 机构后缀 c 连词 k 后缀 nit 教育相关机构 cc 并列连词 l 习用语 nl 名词性惯用语 d 副词 m 数词 nm 物品名 dg 辄,俱,复之类的副词 mg 数语素 nmc 化学品名 dl 连语 Mg 甲乙丙丁之类的数词 nn 工作相关名词 e 叹词 mq 数量词 nnd 职业 end 仅用于终##终 n 名词 nnt 职务职称 f 方位词 nb 生物名 nr 人名 g 学术词汇 nba 动物名 nr1 复姓 gb 生物相关词汇 nbc 动物纲目 nr2 蒙古姓名 gbc 生物类别 nbp 植物名 nrf 音译人名 P 2 =========== === ========================== ===== ============== nrj 日语人名 qg 量词语素 ud 助词 ns 地名 qt 时量词 ude1 的 底 nsf 音译地名 qv 动量词 ude2 地 nt 机构团体名 r 代词 ude3 得 ntc 公司名 rg 代词性语素 udeng 等 等等 云云 ntcb 银行 Rg 古汉语代词性语素 udh 的话 ntcf 工厂 rr 人称代词 ug 过 ntch 酒店宾馆 ry 疑问代词 uguo 过 nth 医院 rys 处所疑问代词 uj 助词 nto 政府机构 ryt 时间疑问代词 ul 连词 nts 中小学 ryv 谓词性疑问代词 ule 了 喽 ntu 大学 rz 指示代词 ulian 连 (“连小学生都会”) nx 字母专名 rzs 处所指示代词 uls 来讲 来说 而言 说来 nz 其他专名 rzt 时间指示代词 usuo 所 o 拟声词 rzv 谓词性指示代词 uv 连词 p 介词 s 处所词 uyy 一样 一般 似的 般 pba 介词“把” t 时间词 uz 着 pbei 介词“被” tg 时间词性语素 uzhe 着 q 量词 u 助词 uzhi 之 P 3 =========== === ========================== ===== ============== v 动词 wb 百分号千分号,全角:% ‰ 半角:% wt 叹号,全角:! vd 副动词 wd 逗号,全角:, 半角:, ww 问号,全角:? vf 趋向动词 wf 分号,全角:; 半角: ; wyy 右引号,全角:” ’ 』 vg 动词性语素 wh 单位符号,全角:¥ $ £ ° ℃ 半角:$ wyz 左引号,全角:“ ‘ 『 vi 不及物动词(内动词) wj 句号,全角:。 x 字符串 vl 动词性惯用语 wky 右括号,全角:) 〕 ] } 》 】 〗 〉 半角: ) ] { > xu 网址URL vn 名动词 wkz 左括号,全角:( 〔 [ { 《 【 〖 〈 半角:( [ { < xx 非语素字 vshi 动词“是” wm 冒号,全角:: 半角: : y 语气词(delete yg) vx 形式动词 wn 顿号,全角:、 yg 语气语素 vyou 动词“有” wp 破折号,全角:—— -- ——- 半角:— —- z 状态词 w 标点符号 ws 省略号,全角:…… … zg 状态词 再来个多query例子: 12345testCases = ['老白的真实身份是什么','盗圣是谁']for sentence in testCases: print(HanLP.segment(sentence)) # [老白/nz, 的/ude1, 真实/a, 身份/n, 是/vshi, 什么/ry]# [盗/vg, 圣/ag, 是/vshi, 谁/ry] 这里的“盗圣”被分开了,分成“盗”和“圣”,一个动词一个形容词,没有被识别成专有名词,是因为训练的时候没有这个样本,的确盗圣这个也很少在其他场景/小说/电视剧等地方出现。 顺便再来看看HanLP的其他功能: 关键词提取 123456document = '老白的真实身份是什么'print(HanLP.extractKeyword(document, 2))# output:[老白, 真实]print(HanLP.extractKeyword(document, 3))# output:[老白, 身份, 真实] 自动摘要 这里的自动摘要也是比较重要的功能,因为比写论文更头疼的是还要写摘要,如果自动摘要技术成熟后,论文的摘要就可以自动生成了。包括读长文章就可以先看摘要再决定要不要深入看下去。 1234print(HanLP.extractSummary('一个月黑风高的杀人夜,传说中的雌雄双煞从天而降,打乱了同福客栈的安稳日子。家世显赫、从小娇生惯养的郭芙蓉,父亲是一代大侠,始终把她笼罩在阴影之下。从小争胜好胜的她,毅然选择了一条离家出走独闯江湖的路,却在第一站,被扣在了同福客栈,从此开始了艰苦卓绝的杂役生涯……', 1))# output# [被扣在了同福客栈] 依存句法分析 123456789HanLP.parseDependency("老白的真实身份是什么")# output# 1 老白 老白 nh nr _ 4 定中关系 _ _# 2 的 的 u u _ 1 右附加关系 _ _# 3 真实 真实 a a _ 4 定中关系 _ _# 4 身份 身份 n n _ 5 主谓关系 _ _# 5 是 是 v v _ 0 核心关系 _ _# 6 什么 什么 r r _ 5 动宾关系 _ _ 词性标注的一个应用方向是在知识图谱里,当你确定词性时,更加能方便把可能的答案揪出来。]]></content>
<tags>
<tag>NLP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[sentiment analysis]]></title>
<url>%2F2019%2F03%2F11%2Fsentiment-analysis%2F</url>
<content type="text"><![CDATA[Sentiment Analysis(情感分析),又称倾向性分析,意见抽取(Opinion extraction),意见挖掘(Opinion mining),情感挖掘(Sentiment mining),主观分析(Subjectivity analysis),它是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程,是NLP中一种常见应用。 大致有以下几种应用场景: 商品评价 在上网购物司空见惯的时代,购物评价铺天盖地,淘宝/京东/大众点评/…,客户的评价(好评positive/差评negative)变得对消费者、店家和app平台越来越重要。很多商户需要及时分析自家产品的评价,发现问题进行产品优化。 社交账号留言 比如在微博上有很多大V或明星要做评控,所以短时间内处理大量评论成为了需求。例如反黑一直是饭圈比较头痛的事情,目前的做法是靠人工进行过滤,Sentiment Analysis就能智能反黑,用数据科学碾压竞争者。 舆情分析 利用公共信息来判断趋势,比如新政策的出台是否受民众喜欢,公众对选举候选人的看法如何。 趋势预测 根据舆论预测选举结果;根据消费者信息指数预测市场趋势;早在2010年,就有学者指出,依靠Twitter公开信息的情感分析来预测股市的涨落,准确率高达87.6%! Sentiment Analysis的本质是表达一种态度/观点(attitude) ,表达观点包含以下3个元素: Holder / source of attitude( = 持有观点的人 ) Aspect / target of attitude( = 观点的角度 ) Type of attitude( = 观点的情绪 ) positive, negative star rating (⭐ ~ ⭐⭐⭐⭐⭐) hate, dislike, don’t care, Like, love (make choice) Sentiment Analysis 的常见处理方式有以下几种: docs -based: 整个观点文本直接判断 sentences / phrases -based: 将文本拆成句子或短语再进行观点和情绪判断 aspects/targets/attributes -based: 先做观点抽取,再做情绪判断。 因此,我们面临的情感分析任务包括如下几类: Simplest task: Is the attitude of this text positive or negative? 基于整段文字/句子/短语 直接判断正负向情绪 More complex: Rank the attitude of this text from 1 to 5 基于整段文字/句子/短语 直接判断情绪层次 Advanced (eg. Aspect-based): Detect the target, source, or complex attitude types 先判断存在哪些观点的角度,再判断情绪倾向 目前NLP被研究的最多的两种语言是英文和中文,英文在网上搜有很多好用的工具在此不做介绍。这里主要介绍对中文文本的情感分析。 情感倾向分析一般我们看评价的时候,如果一个商品好评多,我们可能买的更放心。下面是些关于某电影的评价: 公开的sentiment analysis体验接口,可以随意感受一下: 腾讯 百度 (推荐👍虽然不像我的风格,但这块百度做的还可以) 开源/好用的工具SnowNLP (🔗链接)GitHub上有开源做情感分析的好用的python包,我们来感受一下。首先请确保自己已经 ‘pip install snownlp’ ,对于nlp选手,这个包应该很熟悉了。不仅可以做情感分析,还可以做中文分词、词性标注、提取关键字等等。 1234from snownlp import SnowNLPs = SnowNLP(u'这个东西真心很赞')print(s.sentiments) # 0.9769551298267365 positive的概率 非常简单,就3行code解决。再来试试其他: 12345678s = SnowNLP(u'这个东西真心很赞,速度非常快,隔天到,送货到家,快递员服务态度很好,值得推荐,点个赞!')print(s.sentiments) # 0.9998864907454681 positive的概率s = SnowNLP(u'布料不好,不建议购买')print(s.sentiments) # 0.11886826035520526 positive的概率s = SnowNLP(u'食物很美味,但是服务员的态度很差劲!总体还可以吧。')print(s.sentiments) # 0.42427513441222864 positive的概率 第二比第一句的正向评论更多,因此positive的概率也更大了。第三句属于负面评价,因此分数越接近0为负面,接近1为正面。 SnowNLP的优点是可以拿自己的数据去训练这个分类器,缺点是在长文本分析时,尤其是出现多个观点时,会有预测不准的情况。 当评价是长文本,也可以先做分句,再对每个句子进行情感分析: 123456789101112s = SnowNLP(u'食物很美味,但是服务员的态度很差劲!总体还可以吧。')for sentence in s.sentences: print(sentence) sentc = SnowNLP(sentence) print(sentc.sentiments) # 食物很美味# 0.7625799472162259# 但是服务员的态度很差劲# 0.059144753698306296# 总体还可以吧# 0.8182792359070481 百度API这里的APP_ID、API_KEY、SECRET_KEY需要自己申请,传送门:开发指南 。这样就可以免费用百度提供的接口了,目前还挺准的,而且最大可接受2048字节,目前缺点是有QPS=5限制,但也比人判断快多了。 123456789101112from aip import AipNlp""" 你的 APPID AK SK """APP_ID = '15593833'API_KEY = 'PBW2w1dveS7x3YMecKSZW'SECRET_KEY = 'AOE75EWZqeI6kM7WMXKesq8i6FzQ'client = AipNlp(APP_ID, API_KEY, SECRET_KEY)result=client.sentimentClassify('我真的觉得武林外传超级好看!每个角色都超级喜欢!一年最少看三遍!老白说上一句我就能接下一句!')print(result)# {'log_id': 4474874689828720427, 'text': '我真的觉得武林外传超级好看!每个角色都超级喜欢!一年最少看三遍!老白说上一句我就能接下一句!', 'items': [{'negative_prob': 0.0638882, 'sentiment': 2, 'positive_prob': 0.936112, 'confidence': 0.858026}]} 这里的返回结果有negative_prob,表示这段话为负面情绪的概率。sentiment=2表示判断结果为正面情绪,为0表示为负面,为1表示为中立情绪。 如果是批量请求: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192#!/usr/bin/env python# -*- coding: utf-8 -*-from aip import AipNlpimport pandas as pdimport time""" 你的 APPID AK SK """APP_ID = '155934'API_KEY = 'PBW2w1dveS7x3YcKSZW0V7'SECRET_KEY = 'AOE75EWZqeI6kM7Kesq8i6FzQruDI'client = AipNlp(APP_ID, API_KEY, SECRET_KEY)# 请求文件source_file = "请求文件路径"source_df = pd.read_excel(source_file)comments = []neg_probs = []pos_probs = []confidences = []sentiments = []complete_count = 0# 请求错误统计err_count = 0err_comment = []start_time = time.time()# 循环请求i = 0while i < len(source_df): comment = source_df["comment_content"][i] try: query_result = client.sentimentClassify(comment[:1024]) except Exception as e: print("query_result:{}".format(query_result)) print("#######请求过程存在问题#######") err_count += 1 err_comment.append(comment) i += 1 continue try: result = query_result['items'][0] neg_prob = result['negative_prob'] pos_prob = result['positive_prob'] confidence = result['confidence'] sentiment = result['sentiment'] except KeyError as e: print("#######请求QPS限制#######") print("i={}".format(i)) continue i += 1 comments.append(comment) neg_probs.append(neg_prob) pos_probs.append(pos_prob) confidences.append(confidence) sentiments.append(sentiment) complete_count += 1 print("总共:{}条".format(len(source_df))) print("请求完成: {}条".format(complete_count)) print("完成进度:{}%".format(round(complete_count / len(source_df) * 100, 2))) cost_mins = (time.time() - start_time) / 60 print("累计用时:{}分钟".format(round(cost_mins, 2))) avg_query_time = complete_count / cost_mins # print("每条请求平均用时:{}".format(avg_query_time)) left_mins = (len(source_df) - complete_count - err_count) / avg_query_time print("预计还需:{}分钟".format(round(left_mins, 2))) print("\n")print("所有请求完成!")print("请求总数量:{}".format(len(source_df)))print("请求过程中存在问题的数量:{}".format(err_count))# 保存结果# 请求成功的结果保存desti_df = pd.DataFrame()desti_df["comment"] = commentsdesti_df["neg_probs"] = neg_probsdesti_df["pos_probs"] = pos_probsdesti_df["confidences"] = confidencesdesti_df["sentiments"] = sentimentsdesti_file = "请求结果保存路径"desti_df.to_excel(desti_file, engine='xlsxwriter')# 请求失败的结果保存err_df = pd.DataFrame()err_file = "请求结果报错保存路径"err_df["comment"] = err_commenterr_df.to_excel(err_file, engine='xlsxwriter') # 如果请求接口里有奇怪字符,保存文件时就使用, engine='xlsxwriter' baseline model关于评价分析最经典的就是kaggle的电影评价题,Bag of Words Meets Bags of Popcorn ,预测一段评论是positive还是negative。稍后会专门为这个比赛写一篇。 可惜整个数据集都是英文的。下面几个为中文的数据集,非常珍贵,供参考: nlp_chinese_corpus自然语言处理与信息检索共享平台爬取商品评论并对商品评论进行情感分类 其他开源项目参考: baidu 的 Senta SentimentPolarityAnalysis vaderSentiment 评论观点抽取 Aspect-Based很多时候,观点经常是多角度的。一段评价可能同时有正面情绪和负面情绪,同时表达了多个观点: 食物很美味,但是服务员的态度很差劲!总体还可以吧。 这条评价里对食物的观点是positive的,对服务的观点是negative的,提及了两个维度。此时对整个评价做简单的分类是不够的。 Aspect / target of attitude( = 观点的角度 ) single dimension 单个角度 many dimensions 多个角度 (food, price, service,……根据不同的产品会有不同的角度) 下面是关于购物评价的 Aspects-based sentiment analysis: 这张图内有两个步骤,首先进行观点抽取,这里有关于价格、服务、口味等观点;其次再进行观点的正负面预测,在最右侧一栏的结果展示里。 给一个百度的观点抽取体验接口:传送门 里面已经有13类行业的各种观点预料训练出来的模型,比如有酒店、美食、房产、汽车等行业。当你把评价输入进去之后,分析结果会把各观点以及情绪都总结出来。 AI Challenger 全球AI挑战赛2018的AI Challenger有一个是关于观点抽取的:细粒度用户评论情感分析。 官网:https://challenger.ai/ 官方的baseline:AI_Challenger_2018 开源的解决方案:AI Challenger 2018 文本挖掘类竞赛相关解决方案及代码汇总 数据集下载:传送门]]></content>
<tags>
<tag>NLP</tag>
<tag>sentiment analysis</tag>
</tags>
</entry>
<entry>
<title><![CDATA[论文笔记 - Convolutional Sequence to Sequence Learning]]></title>
<url>%2F2018%2F11%2F01%2F%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0-Convolutional-Sequence-to-Sequence-Learning%2F</url>
<content type="text"><![CDATA[最近读了两篇2017年很出名的论文,分别是Facebook的《Convolutional Sequence to Sequence Learning》和Google的《Attention is All You Need》。这两篇让我改观开始变得对读论文有好感,可想而知精彩程度。本质上都是抛弃了RNN做Seq2Seq的机器翻译任务。本篇先进行《Convolutional Sequence to Sequence Learning》的论文走读。 面向读者:本身就对这篇论文感兴趣,但还是有不理解的地方。 论文摘要 抛弃RNN,只用CNN做Seq2Seq(机器翻译)任务。但是全篇少不了CNN与RNN的对比描述。 RNN是链式结构(Chain Structure),不能并行训练,CNN(Hierarchical Structure)可以,并且大大降低计算复杂度。 论文目录如下: 1234567891011121314151617181920211. Introduction2. Recurrent Sequence to Sequence Learning3. A Convolutional Architecture |- 3.1 Position Embedding |- 3.2 Convolutional Block Structure |- 3.3 Multi-step Attention |- 3.4 Normalization Strategy |- 3.5 Initialization4. Experimental Setup |- 4.1 Datasets |- 4.2 Model Parameters and Optimization |- 4.3 Evaluation5. Results |- 5.1 Recurrent vs. Convolutional Models |- 5.2 Ensemble Results |- 5.3 Generation Speed |- 5.4 Position Embedding |- 5.5 Multi-step Attention |- 5.6 Kernel size and Depth |- 5.7 Summarization6. Conclusion and Future Work (主要详细展开第三节的内容。本篇以走读的形式,一句话quote出来,再解释理解,写完整理删,挑我觉得重要的句子讲,读这篇文章最好是对这篇论文有一定了解,或者是左边是论文,右边是这篇文章,下面引用的句子都是论文里的文字,之前读过很多论文,也是在纸上画画,这个是第一篇写论文笔记) 1. IntroductionCNN vs RNN 链式/层级 结构,并行运算 上下文相关 输入输出固定长度 计算复杂度 RNN是链式结构,CNN是层级结构 Compared to recurrent layers, convolutions create representations for fixed size contexts, however, the effective context size of the network can easily be made larger by stacking several layers on top of each other. This allows to precisely control the maximum length of dependencies to be modeled. CNN处理的input受限,需要是等长的文本,fixed size,但是只要往上堆叠层数就能处理更长的文本。 这样的结构能控制最大长度数值。 Convolutional networks do not depend on the computations of the previoustime step and therefore allow parallelization over every element in a sequence. This contrasts with RNNs which maintain a hidden state of the entire past that prevents parallel computation within a sequence. 句子中的每个单词并行运算,不依赖前个词的计算。与RNN的隐藏状态相反。 Multi-layer convolutional neural networks create hierarchical representations over the input sequence in which nearby input elements interact at lower layers while distant elements interact at higher layers. 为了获取更多的信息,使用的是多层的CNN结构。越低层的处理的词在句子中更近,越高层处理的词距更大。 Hierarchical structure provides a shorter path to capture long-range dependencies compared to the chain structure modeled by recurrent networks, e.g. we can obtain a feature representation capturing relationships within a window of n words by applying only O(n/k) convolutional operations for kernels of width k, compared to a linear number O(n) for recurrent neural networks. 这种层级结构缩短了获取长距离的信息步骤。 Inputs to a convolutional network are fed through a constant number of kernels and non-linearities, whereas recurrent networks apply up to n operations and non-linearities to the first word and only a single set of operations to the last word. CNN处理数据:常量个kernel和非线性处理 RNN:变量个n步骤,第一个词非线性,最后一个词做单一处理 In this paper we propose an architecture for sequence to sequence modeling that is entirely convolutional. Our model is equipped with gated linear units (Dauphin et al., 2016) and residual connections (He et al., 2015a).We also use attention in every decoder layer and demonstrate that each attention layer only adds a negligible amount of overhead. 完全靠卷积 顺带GLU/residual connections/attention 2. Recurrent Sequence to Sequence Learning介绍RNN的运算过程,很简短的带过,因为之前有专门介绍 Input Sequence $x = (x_1,…,x_m)$ Encoder Embedding $w = (w_!,…,w_m)$ State Representation $z = (z_1,…,z_m)$ ================================================= [Encoder] Conditional Input $c = (c_1,…,c_i,…)$ ================================================= [Decoder] Hidden State $h = (h_1,…,h_n)$ Decoder Embedding $g = (g_1,…,g_n)$ Output Sequence $y = (y_1,…,y_n)$ 因为是Encoder-Decoder结构,所以运算都是上下对称的。 w和g分别为input sequence和output sequence的 3. A Convolutional Architecture正文从这个才刚刚开始。 尽量用图示表示]]></content>
<tags>
<tag>CNN</tag>
<tag>NLP</tag>
<tag>seq2seq</tag>
</tags>
</entry>
<entry>
<title><![CDATA[NLP笔记 - LSTM 结构详解]]></title>
<url>%2F2018%2F11%2F01%2FNLP%E7%AC%94%E8%AE%B0-LSTM%2F</url>
<content type="text"><![CDATA[LSTM(Long Short Term Memory networks),长短期记忆网络是经典RNN的升级版,原则上是RNN的一种。用来解决 long-term dependency问题,现被广泛使用。 Long-Term Dependencies传统RNN的链式结构如下: RNN最大优点就是能利用之前的信息来预测当前内容。比如要对句子 “云朵漂浮在天空” 的最后一个词“天空”进行预测。其实,光靠“云朵”这个词就能明显的知道预测词是“天空”。如下图,“云朵”和“天空”的位置是很近的。这样的操作在短文本里是比较有效实用的。 但是还有很多是长文本的预测。比如,“我 从小在中国长大,我很喜欢那里的美食……我能说很流利的中文。” 对这个长文本进行最后一个词“中文”的预测。根据附近的文字“说很流利的”,预测词很可能是一个语种,至于是什么语种还需要回归到最初的文本才能确定下来,如下图。然而这个预测词与最初的文本的距离可能是非常大的。 然而,预测词与相关文本的间距越大,RNN就越难去获取相关信息。这就是Long-Term Dependencies 问题。 因此就有了LSTM,来解决这个Long-Term Dependencies问题。 下面是传统的RNN的链式内部结构,很简单明了,比如有一个tanh层。 LSTM在大体上也是一个链式结构,但在内部稍做改进。与上面不同的是有4层网络结构。 先不要觉得这个内部结构复杂,接下来会拆解开来一步步说明。下图是一些标记符号: LSTM 核心结构LSTM结构主线是下面的 C_{t-1}→C_{t} 的这条水平线。以下称为C线。这条线就像个传送带贯穿整个链式结构。这样使得信息流通且保全信息内容。 对于这条C线,LSTM本身没有删减文本信息的能力,信息量的删减是由各种“门”(gates)调控的。 门是对信息筛选的一种操作,有选择性的让信息流过。“门”由一个sigmoid层(a sigmoid neural net layer)和一个点乘组成(pointwise multiplication operation)。 sigmoid层:输出为0-1的数,用来描述筛选信息的程度。0意味着不让信息流通,1意味着让所有信息流通。 一个LSTM有3个这样的门,来保护和控制这条C线上的信息流。 LSTM 拆解GATE 1第一个门是用来决定在C线上丢弃哪些信息,由一个叫“forget gate layer”的sigmoid层构成,取决于h_{t−1}和x_{t},它的输出是一个0-1的数值。比如在文本中,前一个句子的主角是个男性,后一个句子的主角是个女性,那么在前一个句子里获取的性别信息应该舍弃。 GATE 2第二个门是用来确定保留哪些信息。这里由两部分组成,第一个是由一个叫“input gate layer”的sigmoid层来决定更新哪些数值,第二个是一个tanh层来创建一个候选C向量,这个向量可以被加到C线中。这两个的组合就形成了C线信息的更新。就像文本主角的性别信息更新。 GATE 3现在来更新C_{t-1}→C_{t}。f_{t}×C_{t-1}是丢弃信息操作,i_{t}×C'_{t}是信息更新操作。这样就完成了主角性别信息的更新。 最后我们需要根据C线决定输出内容。首先是一个sigmoid层进行输出内容筛选。其次使其经过tanh处理再对sigmoid的输出进行点乘,就输出我们决定输出的内容。比如更新的是性别信息是一个名词,但预测词要求可能是一个代词,就需要进行将“男/女”信息转换成“他/她”的预测。 LSTM 变种以上介绍的是最普通的LSTM,还有其它的LSTM的变种,对内部结构做轻微的改变。 peephole connections Gated Recurrent Unit Deep LSTM 写在最后在一系列RNN取得的优秀成果背后,有相当多使用的是LSTM结构。RNN在训练时会遇到gradient explode/vanishing,导致无法训练。实际中,使用更多的是改良后的LSTM,GRU等。希望上面的拆解使得LSTM看起来平易近人。 LSTM是一个重大的进步,继LSTM之后,还有一个重大的进步,就是Attention,注意力机制。接下来还会更具体地介绍Attention,敬请期待!😘 参考 Understanding LSTM Networks]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>NLP</tag>
<tag>RNN</tag>
<tag>LSTM</tag>
</tags>
</entry>
<entry>
<title><![CDATA[NLP笔记 - RNN 结构详解]]></title>
<url>%2F2018%2F10%2F31%2FNLP%E7%AC%94%E8%AE%B0-RNN%2F</url>
<content type="text"><![CDATA[面向读者:深度学习 / NLP入门选手 NLP里最常用最传统的深度学习模型就是循环神经网络 RNN(Recurrent Neural Network)。这个模型的命名已经说明了数据处理方法,是按顺序按步骤读取的。与人类理解文字的道理差不多,看书都是一个字一个字,一句话一句话去理解的。 本文将介绍RNN及其一些重要的RNN变种模型: RNN Encoder-Decoder( = Seq-to-Seq ) Attention Mechanism RNN 多结构详解CNN vs RNN CNN 需要固定长度的输入、输出,RNN 的输入和输出可以是不定长且不等长的 CNN 只有 one-to-one 一种结构,而 RNN 有多种结构,如下图: 1. one-to-one 最基本的单层网络,输入是x,经过变换Wx+b和激活函数f得到输出y。 2. one-to-n输入不是序列而输出为序列的情况,只在序列开始进行输入计算: 图示中记号的含义是: 圆圈或方块表示的是向量。 一个箭头就表示对该向量做一次变换。如上图中h_{0}和x分别有一个箭头连接,就表示对h_{0}和x各做了一次变换。 还有一种结构是把输入信息X作为每个阶段的输入: 下图省略了一些X的圆圈,是一个等价表示: 这种 one-to-n 的结构可以处理的问题有: 从图像生成文字(image caption),此时输入的X就是图像的特征,而输出的y序列就是一段句子,就像看图说话等 从类别生成语音或音乐等 3. n-to-n最经典的RNN结构,输入、输出都是等长的序列数据。 假设输入为X=(x_{1}, x_{2}, x_{3}, x_{4}),每个x是一个单词的词向量。 为了建模序列问题,RNN引入了隐状态h(hidden state)的概念,h可以对序列形的数据提取特征,接着再转换为输出。先从h_{1}的计算开始看: h_{2}的计算和h_{1}类似。要注意的是,在计算时,每一步使用的参数U、W、b都是一样的,也就是说每个步骤的参数都是共享的,这是RNN的重要特点,一定要牢记。 依次计算剩下来的(使用相同的参数U、W、b): 这里为了方便起见,只画出序列长度为4的情况,实际上,这个计算过程可以无限地持续下去。得到输出值的方法就是直接通过h进行计算: 正如之前所说,一个箭头就表示对对应的向量做一次类似于f(Wx+b)的变换,这里的这个箭头就表示对h_{1}进行一次变换,得到输出y_{1}。 剩下的输出类似进行(使用和y_{1}同样的参数V和c): 这就是最经典的RNN结构,它的输入是x_{1}, x_{2}, .....x_{n},输出为y_{1}, y_{2}, ...y_{n},也就是说,输入和输出序列必须要是等长的。由于这个限制的存在,经典RNN的适用范围比较小,但也有一些问题适合用经典的RNN结构建模,如: 计算视频中每一帧的分类标签。因为要对每一帧进行计算,因此输入和输出序列等长。 输入为字符,输出为下一个字符的概率。这就是著名的Char RNN(详细介绍请参考:The Unreasonable Effectiveness of Recurrent Neural Networks,Char RNN可以用来生成文章,诗歌,甚至是代码,非常有意思)。 4. n-to-one要处理的问题输入是一个序列,输出是一个单独的值而不是序列,应该怎样建模呢?实际上,我们只在最后一个h上进行输出变换就可以了: 这种结构通常用来处理序列分类问题。如输入一段文字判别它所属的类别,输入一个句子判断其情感倾向,输入一段视频并判断它的类别等等。 Encoder-Decodern-to-m还有一种是 n-to-m,输入、输出为不等长的序列。 这种结构是Encoder-Decoder,也叫Seq2Seq,是RNN的一个重要变种。原始的n-to-n的RNN要求序列等长,然而我们遇到的大部分问题序列都是不等长的,如机器翻译中,源语言和目标语言的句子往往并没有相同的长度。为此,Encoder-Decoder结构先将输入数据编码成一个上下文语义向量c: 语义向量c可以有多种表达方式,最简单的方法就是把Encoder的最后一个隐状态赋值给c,还可以对最后的隐状态做一个变换得到c,也可以对所有的隐状态做变换。 拿到c之后,就用另一个RNN网络对其进行解码,这部分RNN网络被称为Decoder。Decoder的RNN可以与Encoder的一样,也可以不一样。具体做法就是将c当做之前的初始状态h_{0}输入到Decoder中: 还有一种做法是将c当做每一步的输入: Encoder-Decoder 应用由于这种Encoder-Decoder结构不限制输入和输出的序列长度,因此应用的范围非常广泛,比如: 机器翻译:Encoder-Decoder的最经典应用,事实上这结构就是在机器翻译领域最先提出的。 文本摘要:输入是一段文本序列,输出是这段文本序列的摘要序列。 阅读理解:将输入的文章和问题分别编码,再对其进行解码得到问题的答案。 语音识别:输入是语音信号序列,输出是文字序列。 Encoder-Decoder 框架Encoder-Decoder 不是一个具体的模型,是一种框架。 Encoder:将 input序列 →转成→ 固定长度的向量 Decoder:将 固定长度的向量 →转成→ output序列 Encoder 与 Decoder 可以彼此独立使用,实际上经常一起使用 因为最早出现的机器翻译领域,最早广泛使用的转码模型是RNN。其实模型可以是 CNN /RNN /BiRNN /LSTM /GRU /… Encoder-Decoder 缺点 最大的局限性:编码和解码之间的唯一联系是固定长度的语义向量c 编码要把整个序列的信息压缩进一个固定长度的语义向量c 语义向量c无法完全表达整个序列的信息 先输入的内容携带的信息,会被后输入的信息稀释掉,或者被覆盖掉 输入序列越长,这样的现象越严重,这样使得在Decoder解码时一开始就没有获得足够的输入序列信息,解码效果会打折扣 因此,为了弥补基础的 Encoder-Decoder 的局限性,提出了attention机制。 Attention Mechanism注意力机制(attention mechanism)是对基础Encoder-Decoder的改良。Attention机制通过在每个时间输入不同的c来解决问题,下图是带有Attention机制的Decoder: 每一个c会自动去选取与当前所要输出的y最合适的上下文信息。具体来说,我们用a_{ij}衡量Encoder中第j阶段的h_{j}和解码时第i阶段的相关性,最终Decoder中第i阶段的输入的上下文信息 c_{i}就来自于所有h_{j}对 a_{ij} 的加权和。 以机器翻译为例(将中文翻译成英文): 输入的序列是“我爱中国”,因此,Encoder中的h_{1}、h_{2}、h_{3}、h_{4}就可以分别看做是“我”、“爱”、“中”、“国”所代表的信息。在翻译成英语时,第一个上下文c_{1}应该和 “我” 这个字最相关,因此对应的 a_{11} 就比较大,而相应的 a_{12}、a_{13}、a_{14} 就比较小。c_{2}应该和“爱”最相关,因此对应的 a_{22}就比较大。最后的c_{3}和h_{3}、h_{4}最相关,因此a_{33}、a_{34}的值就比较大。 至此,关于Attention模型,只剩最后一个问题了,那就是:这些权重 a_{ij} 是怎么来的? 事实上,a_{ij}同样是从模型中学出的,它实际和Decoder的第i-1阶段的隐状态、Encoder第j个阶段的隐状态有关。 同样还是拿上面的机器翻译举例,a_{1j}的计算(此时箭头就表示对h’和 h_{j}同时做变换): a_{2j} 的计算: a_{3j} 的计算: 以上就是带有Attention的Encoder-Decoder模型计算的全过程。 Attention 的优点: 在机器翻译时,让生词不只是关注全局的语义向量c,增加了“注意力范围”。表示接下来输出的词要重点关注输入序列种的哪些部分。根据关注的区域来产生下一个输出。 不要求编码器将所有信息全输入在一个固定长度的向量中。 将输入编码成一个向量的序列,解码时,每一步选择性的从序列中挑一个子集进行处理。 在每一个输出时,能够充分利用输入携带的信息,每个语义向量c_{i}不一样,注意力焦点不一样。 Attention 的缺点 需要为每个输入输出组合分别计算attention。50个单词的输出输出序列需要计算2500个attention。 attention在决定专注于某个方面之前需要遍历一遍记忆再决定下一个输出是以什么。 Attention的另一种替代方法是强化学习,来预测关注点的大概位置。但强化学习不能用反向传播算法端到端的训练。 Attention可视化Attention的可视化是一件非常cool的事!😝下面推荐一些优秀的attention相关论文。 paper: Attention Is All You Need下图是句子的单词之间的attention联系,颜色深浅表示大小。 paper: Show, Attend and Tell注意每个句子的不同单词在图片中的attention标注: paper: Convolutional Sequence to Sequence Learning机器翻译中attention数值的可视化,一般是随着顺序强相关的。 其它paper: [Grammar as a Foreign Language] [Teaching Machines to Read and Comprehend] 下面这张Attention热力图表达出了,当人在阅读的时候,注意力大多数放在了文字标题或者段落第一句话,还是蛮符合现实情况的。 Attention Mechanism是很早的概念了,但随着2017年谷歌的一篇《Attention Is All You Need》被完全引爆。随后会写这篇论文的详细走读和过程推导😎,敬请期待~ 参考 完全图解RNN、RNN变体、Seq2Seq、Attention机制RNN梯度消失和爆炸的原因]]></content>
<tags>
<tag>NLP</tag>
<tag>RNN</tag>
</tags>
</entry>
<entry>
<title><![CDATA[NLP实战 - 基于SimNet的Quora问句语义匹配]]></title>
<url>%2F2018%2F09%2F09%2FNLP%E5%AE%9E%E6%88%98-%E5%9F%BA%E4%BA%8ESimNet%E7%9A%84Quora%E9%97%AE%E5%8F%A5%E8%AF%AD%E4%B9%89%E5%8C%B9%E9%85%8D%2F</url>
<content type="text"><![CDATA[Quora Question Pairs是kaggle里的问句语义匹配比赛。这场比赛对于nlp选手应该不陌生了,数据集也是大家入门nlp必备。本文在深度语义匹配使用的是百度开源的语义匹配框架AnyQ里的SimNet。 环境说明 Linux python 2.7 TensorFlow 1.7.0 CPU Jupyter Notebook 下载AnyQ首先需要有git,如果没有可以点这里下载。在Linux环境选定路径下敲入git clone https://github.com/baidu/AnyQ进行AnyQ的下载。 下载完,查看SimNet的路径是AnyQ/tools/simnet/train/tf/ SimNet 的结构如下: 123456789simnet |-tf |- date //示例数据 |- examples //示例配置文件 |- layers //网络中使用操作层的实现 |- losses //损失函数实现 |- nets //网络结构实现 |- tools //数据转化及评价工具 |- util //工具类 ❤另外已经专门写了一篇关于SimNet的代码走读,强烈推荐打开那篇文章放在旁边与这篇一起看,点这里查看。 另外,在Linux查看/修改代码,推荐jupyter notebook。 其它说明: 保存模型文件的路径需要自己手动添加,在目录上新建model和pointwise文件夹: 12345678910111213simnet |-tf |- date |- examples |- layers |- losses |- nets |- tools |- util # 新建下面的文件夹 |- model |- pointwise 下载数据集请点击 Quora 进行数据集下载。本文只用到训练数据集,所以下载train.csv即可。train.csv还不能直接作为SimNet的输入数据,需要做词嵌入等数据预处理。 词嵌入处理SimNet的训练数据是有格式要求的。具体请查看SimNet的README.md。这一步数据处理也可以在win环境下操作。由于Quora训练集是两个问句列加一个label列,适合SimNet的pointwise数据格式。 pointwise数据格式:数据包含三列,依次为Query1的ID序列(ID间使用空格分割),Query2的ID序列(ID间使用空格分割),Label,每列间使用TAB分割,例如; 1231 1 1 1 1 2 2 2 2 2 01 1 1 1 1 1 1 1 1 1 1... pointwise需要问句都以id的形式,所以word embedding选择词袋法(BOW)。 新建一个quora.py文件来做词嵌入处理,先处理了空(null)问题,再预览10个问题对(question pairs)。 输入: 123456789101112131415161718192021222324252627282930313233343536# -*- coding: utf-8 -*-"""Created on Wed Aug 29 16:50:59 2018@author: Yi"""import osos.chdir("C:/Users/Yi/Desktop/nlp/quora") # quora.py的路径import pandas as pdimport numpy as npimport nltkfrom nltk.corpus import stopwordsfrom nltk.stem import SnowballStemmerimport refrom string import punctuationtrain = pd.read_csv("data/train.csv") # 共404290个问句对#test = pd.read_csv("data/test.csv")# Check for any null valuesprint(train.isnull().sum())#print(test.isnull().sum())# Add the string 'empty' to empty stringstrain = train.fillna('empty')#test = test.fillna('empty')# Preview some of the pairs of questionsa = 0 for i in range(a,a+10): print(train.question1[i]) print(train.question2[i]) print() 输出: 1234567891011121314151617181920212223242526272829What is the step by step guide to invest in share market in india?What is the step by step guide to invest in share market?What is the story of Kohinoor (Koh-i-Noor) Diamond?What would happen if the Indian government stole the Kohinoor (Koh-i-Noor) diamond back?How can I increase the speed of my internet connection while using a VPN?How can Internet speed be increased by hacking through DNS?Why am I mentally very lonely? How can I solve it?Find the remainder when [math]23^{24}[/math] is divided by 24,23?Which one dissolve in water quikly sugar, salt, methane and carbon di oxide?Which fish would survive in salt water?Astrology: I am a Capricorn Sun Cap moon and cap rising...what does that say about me?I'm a triple Capricorn (Sun, Moon and ascendant in Capricorn) What does this say about me?Should I buy tiago?What keeps childern active and far from phone and video games?How can I be a good geologist?What should I do to be a great geologist?When do you use シ instead of し?When do you use "&" instead of "and"?Motorola (company): Can I hack my Charter Motorolla DCX3400?How do I hack Motorola DCX3400 for free internet? 继续做同义词替换、停用词处理、删除介词等数据清洗,输入: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111stop_words = ['the','a','an','and','but','if','or','because','as','what','which','this','that','these','those','then', 'just','so','than','such','both','through','about','for','is','of','while','during','to','What','Which', 'Is','If','While','This']def text_to_wordlist(text, remove_stop_words=True, stem_words=False): # Clean the text, with the option to remove stop_words and to stem words. # Clean the text text = re.sub(r"[^A-Za-z0-9]", " ", text) text = re.sub(r"what's", "", text) text = re.sub(r"What's", "", text) text = re.sub(r"\'s", " ", text) text = re.sub(r"\'ve", " have ", text) text = re.sub(r"can't", "cannot ", text) text = re.sub(r"n't", " not ", text) text = re.sub(r"I'm", "I am", text) text = re.sub(r" m ", " am ", text) text = re.sub(r"\'re", " are ", text) text = re.sub(r"\'d", " would ", text) text = re.sub(r"\'ll", " will ", text) text = re.sub(r"60k", " 60000 ", text) text = re.sub(r" e g ", " eg ", text) text = re.sub(r" b g ", " bg ", text) text = re.sub(r"\0s", "0", text) text = re.sub(r" 9 11 ", "911", text) text = re.sub(r"e-mail", "email", text) text = re.sub(r"\s{2,}", " ", text) text = re.sub(r"quikly", "quickly", text) text = re.sub(r" usa ", " America ", text) text = re.sub(r" USA ", " America ", text) text = re.sub(r" u s ", " America ", text) text = re.sub(r" uk ", " England ", text) text = re.sub(r" UK ", " England ", text) text = re.sub(r"india", "India", text) text = re.sub(r"switzerland", "Switzerland", text) text = re.sub(r"china", "China", text) text = re.sub(r"chinese", "Chinese", text) text = re.sub(r"imrovement", "improvement", text) text = re.sub(r"intially", "initially", text) text = re.sub(r"quora", "Quora", text) text = re.sub(r" dms ", "direct messages ", text) text = re.sub(r"demonitization", "demonetization", text) text = re.sub(r"actived", "active", text) text = re.sub(r"kms", " kilometers ", text) text = re.sub(r"KMs", " kilometers ", text) text = re.sub(r" cs ", " computer science ", text) text = re.sub(r" upvotes ", " up votes ", text) text = re.sub(r" iPhone ", " phone ", text) text = re.sub(r"\0rs ", " rs ", text) text = re.sub(r"calender", "calendar", text) text = re.sub(r"ios", "operating system", text) text = re.sub(r"gps", "GPS", text) text = re.sub(r"gst", "GST", text) text = re.sub(r"programing", "programming", text) text = re.sub(r"bestfriend", "best friend", text) text = re.sub(r"dna", "DNA", text) text = re.sub(r"III", "3", text) text = re.sub(r"the US", "America", text) text = re.sub(r"Astrology", "astrology", text) text = re.sub(r"Method", "method", text) text = re.sub(r"Find", "find", text) text = re.sub(r"banglore", "Banglore", text) text = re.sub(r" J K ", " JK ", text) # Remove punctuation from text text = ''.join([c for c in text if c not in punctuation]) # Optionally, remove stop words if remove_stop_words: text = text.split() text = [w for w in text if not w in stop_words] text = " ".join(text) # Optionally, shorten words to their stems if stem_words: text = text.split() stemmer = SnowballStemmer('english') stemmed_words = [stemmer.stem(word) for word in text] text = " ".join(stemmed_words) # Return a list of words return(text)def process_questions(question_list, questions, question_list_name, dataframe): '''transform questions and display progress''' for question in questions: question_list.append(text_to_wordlist(question)) if len(question_list) % 100000 == 0: progress = len(question_list)/len(dataframe) * 100 print("{} is {}% complete.".format(question_list_name, round(progress, 1))) train_question1 = []process_questions(train_question1, train.question1, 'train_question1', train)train_question2 = []process_questions(train_question2, train.question2, 'train_question2', train)#test_question1 = []#process_questions(test_question1, test.question1, 'test_question1', test)##test_question2 = []#process_questions(test_question2, test.question2, 'test_question2', test)# Preview some transformed pairs of questionsa = 0 for i in range(a,a+10): print(train_question1[i]) print(train_question2[i]) print() 输出处理之后的10个问题对: 1234567891011121314151617181920212223242526272829step by step guide invest in share market in Indiastep by step guide invest in share marketstory Kohinoor Koh i Noor Diamondwould happen Indian government stole Kohinoor Koh i Noor diamond backHow can I increase speed my internet connection using VPNHow can Internet speed be increased by hacking DNSWhy am I mentally very lonely How can I solve itfind remainder when math 23 24 math divided by 24 23one dissolve in water quickly sugar salt methane carbon di oxidefish would survive in salt waterastrology I am Capricorn Sun Cap moon cap rising does say meI am triple Capricorn Sun Moon ascendant in Capricorn does say meShould I buy tiagokeeps childern active far from phone video gamesHow can I be good geologistshould I do be great geologistWhen do you use insteadWhen do you use insteadMotorola company Can I hack my Charter Motorolla DCX3400How do I hack Motorola DCX3400 free internet 继续删除自定义停用词,删除只出现过一次的词,将问题1和问题2合并成一个大语料库,输入: 123456789101112131415import itertoolsraw_corpus = list(itertools.chain.from_iterable([train_question1,train_question2]))#[train_question1,train_question2]stoplist = stop_wordstexts = [[word for word in document.lower().split() if word not in stoplist] for document in raw_corpus]from collections import defaultdictfrequency = defaultdict(int)for text in texts: for token in text: frequency[token] += 1 precessed_corpus = [[token for token in text if frequency[token] > 1] for text in texts] 形成字典,输入: 12345from gensim import corporadictionary = corpora.Dictionary(precessed_corpus)print(dictionary)print(dictionary.token2id) 输出字典: 1Dictionary(52069 unique tokens: ['vicodin', 'mermaid', 'kgb', 'dusk', 'glonass']...) 输入一个新文本,试一下这个字典,输入: 12345new_doc = "would happen Indian government stole Kohinoor Koh i Noor diamond back"new_vec = dictionary.doc2bow(new_doc.lower().split())#dictionary.doc2idx(new_doc.lower().split())print(new_vec) #列表中每个元组中,第一个元素表示字典中单词的ID,第二个表示在这个句子中这个单词出现的次数。 输出: 1234567891011[(8, 1), (9, 1), (10, 1), (11, 1), (12, 1), (107, 1), (186, 1), (226, 1), (416, 1), (828, 1), (4496, 1)] 感觉还可以,那么将quora的所有问题对的语料都用字典里的id代替,输入: 12345bow_corpus = [dictionary.doc2idx(text) for text in precessed_corpus]bow_corpus_plus_1 = [[i+1 for i in bow_corpu] for bow_corpu in bow_corpus]bow_corpus_str = [[str(i) for i in bow_corpu_plus] for bow_corpu_plus in bow_corpus_plus_1]bow_corpus_join = [' '.join(bow_corpus_) for bow_corpus_ in bow_corpus_str] 由于语料库是问题1和问题2按顺序组成,那么用id代替后的语料库前一半的是词袋处理后问题1,后一半是词袋处理后问题2,最终恢复到pointwise格式的数据,输入: 123456789101112131415# 生成文件pointwise_train = pd.DataFrame(bow_corpus_join[:404290], columns = ['question1'])pointwise_train['question2'] = bow_corpus_join[404290:]pointwise_train['is_duplicate'] = train['is_duplicate']# 防止空(null)问题pointwise_train = pointwise_train[[len(i)>0 for i in pointwise_train['question1']]]pointwise_train = pointwise_train[[len(i)>0 for i in pointwise_train['question2']]]# 拆分训练集和测试集size = round(len(pointwise_train)*0.8) # 比例为8:2# tsv格式的数据文件pointwise_train[:size].to_csv('data/train_0829.tsv',sep = '\t', index=False, header=False)pointwise_train[size:].to_csv('data/test_0829.tsv',sep = '\t', index=False, header=False) 得到tsv格式的train_0829.tsv,test_0829.tsv,可以随便命名。 数据准备切换到Linux,将嵌入完的 tsv 数据集放入AnyQ/tools/simnet/train/tf/data路径下: 12345simnet |-tf |- date //示例数据,tsv格式,没有表头 |- train_0829.tsv //训练集数据 |- test_0829.tsv //测试集数据 按照下图路径AnyQ/tools/simnet/train/tf/,新建 run_convert_data.sh 脚本文件, 其实就是把原来的run_train.sh里转换数据的命令拿出来。因为之后需要多模型跑一样的数据,数据转换做一次就够了,内容如下: 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail echo "convert train data"python ./tools/tf_record_writer.py pointwise ./data/待转换的训练数据文件名train_0829.tsv ./data/已转换的训练数据文件名convert_train_0829 0 32echo "convert test data"python ./tools/tf_record_writer.py pointwise ./data/待转换的测试数据文件名test_0829.tsv ./data/已转换的测试数据文件名convert_test_0829 0 32echo "convert data finish" 在Linux黑命令框里敲入命令./run_convert_data.sh,如果有permission denied情况,先使用chmod 777 文件名,在这里是chmod 777 run_convert_data.sh。如果成功将打印出: 123convert train dataconvert test dataconvert data finish 在data文件夹目录下,新增两个转换后的数据文件,convert_train_0829 和 convert_test_0829。 修改代码修改配置文件用 jupyter notebook 打开 examples 文件夹下的所有形如 xxx-pointwise.json的配置文件,修改以下几个参数数值: data_size = 323273 , 因为train_0829.tsv有 323273 条样本 vocabulary_size = 1000000 batch_size = 800 num_epochs = 1 print_iter = 10 train_file = data/convert_train_0829 test_file = data/convert_test_0829 以上,只是修改模型训练的配置参数,还得另外修改模型检验的配置参数,打开AnyQ/tools/simnet/train/tf/目录下的 tf_simnet.py,找到def predict(conf_dict),找到如下代码(应该在第90行): 12conf_dict.update({"num_epochs": "1", "batch_size": "1", "shuffle": "0", "train_file": conf_dict["test_file"]}) 将其修改成: 12conf_dict.update({"num_epochs": "1", "batch_size": "400", "shuffle": "0", "train_file": conf_dict["test_file"]}) 保存,关闭文件。 修改保存模型文件规则default的代码将在每个epoch迭代时保存一个模型,且最终跑完还会保存一个模型。由于模型文件过大,所以将把代码修改成只保存最后跑完的模型,如果不需要可不做此修改。打开utils文件夹下的controler.py,将100-104行隐去: 12345# if step % epoch_iter == 0:# print("save model epoch%d" % (epoch_num))# save_path = saver.save(sess, # "%s/%s.epoch%d" % (model_path, model_file, epoch_num))# epoch_num += 1 修改打印命令default的代码在模型训练过程中,每一个print_iter会打印出一个loss值,在模型检验过程中,最后会打印出一个accuracy值。但是为了观察跑迭代的速度和精度,还需要在每次报loss的时候,打印出每个print_iter花了几秒钟(如不需要此功能可不做修改)。打开utils文件夹下的controler.py,将90-99行代码修改成如下: 12345678910111213epoch_num = 1last_timestamp = datetime.datetime.now() # 增加的代码while not coord.should_stop(): try: step += 1 c, _= sess.run([loss, optimizer]) avg_cost += c if step % print_iter == 0: now_timestamp = datetime.datetime.now() # 增加的代码 print("step: %d, loss: %4.4f (%4.2f sec/print_iter)" % (step,(avg_cost / print_iter),(now_timestamp-last_timestamp).seconds)) # 修改的代码 avg_cost = 0.0 last_timestamp = now_timestamp # 增加的代码 保存,关闭文件。 比对模型效果在AnyQ/tools/simnet/train/tf/路径增加 .sh 文件。因为SimNet目前有7个可选择的网络,分别是bow, cnn, knrm, lstm, mmdnn, mvlstm, pyramid,分别与nets文件夹里的文件一一对应,所以每种任务都有7个 .sh 脚本。任务类型分别是 train/predict/freeze,对应模型训练,模型检验,模型结果示意。 增加模型训练任务的 .sh文件以cnn为例,新建run_train_cnn.sh,内容如下: 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail in_task_type='train'in_task_conf='./examples/cnn-pointwise.json'python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf 增加模型验证任务的 .sh文件以cnn为例,新建run_predict_cnn.sh,内容如下: 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail in_task_type='predict'in_task_conf='./examples/cnn-pointwise.json'python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf 增加模型结果示意任务的 .sh文件以cnn为例,新建run_freeze_cnn.sh,内容如下: 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail in_task_type='freeze'in_task_conf='./examples/cnn-pointwise.json'python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf 最终,生成7个 run_train_xxx.sh 文件,7个 run_predict_xxx.sh文件,7个 run_freeze_xxx.sh文件,或者选择性生成几个,示意如下图: 切换回Linux命令界面,开始运行各种命令,结果如配图。 bow ./run_train_bow.sh ./run_predict_bow.sh cnn ./run_train_cnn.sh ./run_predict_cnn.sh knrm ./run_train_knrm.sh ./run_predict_knrm.sh lstm ./run_train_lstm.sh ./run_predict_lstm.sh mmdnn ./run_train_mmdnn.sh ./run_predict_mmdnn.sh mvlstm ./run_train_mvlstm.sh ./run_predict_mvlstm.sh pyramid ./run_train_pyramid.sh ./run_predict_pyramid.sh 可自行尝试./run_freeze_xxx.sh命令系列。当跑完上面所有命令时,原文件夹中就自动形成了预测文件,如下: 随便打开其中一个看一下: 如下是自动保存的模型文件: 也会自动生成log文件夹,运行freeze任务后会自动生成graph文件夹,把任务结果保存在文件夹里。 优缺点缺点: 由于SimNet的数据格式有要求,将文本都以ID格式代替,因此词嵌入使用的是词袋bow处理。 在前期文本清洗的规则可以再完善些。 呃。。。。百度(摊手🤷♀️ 优点: SimNet是一个集成体,有很多深度模型可以选择。 写在最后 跑了个SimNet流程,仅作效果比对,没有追求精度的提升。后来我有尝试把batch_size调成30,那么将有10000多步,精度有几个百分点的提升,但还远远不够。所以任重道远呀,还有很多需要学习的。 在本文没有用到测试集,如果要参加比赛,在词嵌入做字典时,是否应该把test测试集的单词也加入到字典里来?这个还没有真的尝试一下,毕竟测试集test.cvs大的吓人! 如有疑问,欢迎留言或者点这里找到我。]]></content>
<tags>
<tag>machine learning</tag>
<tag>kaggle</tag>
<tag>quora</tag>
<tag>Semantic Matching</tag>
<tag>NLP</tag>
<tag>AnyQ</tag>
</tags>
</entry>
<entry>
<title><![CDATA[代码走读 - 百度开源智能问答框架 AnyQ]]></title>
<url>%2F2018%2F09%2F06%2F%E4%BB%A3%E7%A0%81%E8%B5%B0%E8%AF%BB-%E7%99%BE%E5%BA%A6%E6%99%BA%E8%83%BD%E9%97%AE%E7%AD%94%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6-AnyQ%2F</url>
<content type="text"><![CDATA[面向对象:想搭建智能问答系统、深度语义匹配的nlp选手。在自己亲手搭建一个之前,学习和走读优秀的框架代码是个不会错的选择。 AnyQ(ANswer Your Questions) :百度QA开源项目,主要包含面向FAQ集合的问答系统框架、文本语义匹配工具SimNet。 框架目录本文重点走读SimNet框架的代码。开源代码地址,点这里。TensorFlow版SimNet的结构如下:(自动屏蔽名字带 ‘pairwise’ 的文件,稍后解释) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647simnet |-tf |- date //示例数据,tsv格式,没有表头 |- train_pointwise_data //训练集数据 |- test_pointwise_data //测试集数据 |- examples //示例配置文件,以模型种类命名,里面的参数需熟知 |- bow-pointwise.json |- cnn-pointwise.json |- knrm-pointwise.json |- lstm-pointwise.json |- mmdnn-pointwise.json |- mvlstm-pointwise.json |- pyramid-pointwise.json |- layers //网络中使用操作层的实现 |- tf_layers.py |- losses //损失函数实现,可放置各种损失函数class |- simnet_loss.py |- nets //网络结构实现,由tf_layers.py不同组合实现 |- bow.py |- knrm.py |- lstm.py |- matchpyramid.py |- mlpcnn.py |- mm_dnn.py |- mvlstm.py |- tools //数据转化及评价工具 |- evaluate.py |- tf_record_reader.py |- tf_record_writer.py |- util //工具类 |- controler.py |- converter.py # 数据转换 |- datafeeds.py # 读取数据 |- utility.py |- README.md //请仔细反复读, |- run_infer.sh //运行predict任务 |- run_train.sh //运行train任务 |- tf_simnet.py //主运行文件 读框架代码的工具,相较于jupyter,spyder,推荐Pycharm。 运行环境 linux,其它系统推荐docker python 2.7 tensorflow 1.7.0 数据类型解释为何屏蔽名字带 ‘pairwise’ 的文件: 语义匹配网络SimNet可以使用Pointwise与Pairwise两种类型的数据进行训练。 Pointwise训练及测试数据格式 训练数据格式:训练数据包含三列,依次为Query1的ID序列(ID间使用空格分割),Query2的ID序列(ID间使用空格分割),Label,每列间使用TAB分割,例如; 1231 1 1 1 1 2 2 2 2 2 01 1 1 1 1 1 1 1 1 1 1... 测试数据格式:Pointwise测试数据格式与训练数据格式相同。 Pairwise训练及测试数据格式 训练数据格式:训练数据包含三列,依次为Query1的ID序列(ID间使用空格分割),Positive Query2的ID序列(ID间使用空格分割),Negative Query3的ID序列(ID间使用空格分割),每列间使用TAB分割,例如; 1231 1 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3... 测试数据格式:测试数据格式包含三列,依次为Query1的ID序列(ID间使用空格分割),Query2的ID序列(ID间使用空格分割),Label,每列间使用TAB分割,例如; 12341 1 1 1 1 1 1 1 1 1 11 1 1 1 1 2 2 2 2 2 03 3 3 3 3 3 3 3 3 3 1... 由于使用的数据集是Quora数据集,为 [问句1,问句2,label] 的Pointwise格式数据集,因此名字带 ‘pairwise’ 的文件暂时都用不上。若是数据集为pairwise,就能用的上了。 .json 配置文件走读准备完数据文件之后,观察配置文件,以cnn-pointwise.json为例。 通过配置文件可以灵活的选择网络类型,数据类型,损失函数以及其他超参数。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253{ "train_data":{ "train_file": "data/convert_train_pointwise_data", //训练文件路径 "data_size": 400, //训练集大小,根据不同的训练数据文件需做改动 "left_slots" : [["left",32]], //left slot的名字及最大长度 "right_slots" : [["right",32]] //right slot的名字及最大长度 }, "model":{ "net_py": "./nets/mlpcnn", //网络对应模块路径 "net_class": "MLPCnn", //网络对应类名 "vocabulary_size": 3, //词典大小,根据词嵌入得出的字典大小需做改动 # 不同的网络net有不同的参数,这些是cnn的参数 "embedding_dim": 128, // Embedding嵌入层维度 "num_filters": 256, //卷机核数量 "hidden_size": 128, //隐藏层大小 "window_size": 3, //卷机核大小 # 损失函数参数 "loss_py": "./losses/simnet_loss", //损失对应模块路径,内有各种损失函数 "loss_class": "SoftmaxWithLoss" //损失对应类名,可选择其它损失函数 }, "global":{ "training_mode": "pointwise", //训练模式,也是数据格式 "n_class": 2, //类别数目,2为二分类 "max_len_left": 32, //Left slot的最大长度 "max_len_right": 32 //Right slot的最大长度 }, "setting":{ "batch_size": 64, // Batch Size,每一步跑的样本数 "num_epochs": 1, // Number of Epochs,重复全体样本的倍数 "thread_num": 6, //线程数 "print_iter": 100, //显示间隔,每100步显示一个loss "model_path": "model/pointwise", //模型保存路径,在框架目录里没有,需要自己新建 "model_prefix": "cnn", //模型保存名前缀 "learning_rate": 0.001, //学习率 "shuffle": 1 //是否打乱数据 }, "test_data":{ "test_file": "data/convert_test_pointwise_data", //测试数据路径 "test_model_file": "model/pointwise/cnn.epoch1", //测试使用模型,要先跑train任务后才有模型文件保存下来,才能做预测 "test_result": "result_cnn_pointwise" //测试结果文件 }, "graph":{ "graph_path": "graph", //freeze任务文件保存路径 "graph_name": "model_cnn_pairwise.protxt" //freeze任务结果文件 }} 参数说明: 假设训练集的 data_size = 1000时,运行模型训练train任务时,设置了num_epochs = 5,batch_size = 50,shuffle = 1,print_iter = 10,那么训练集最终的样本量 = data_size num_epochs =5000,重复了5倍原有样本量,shuffle = 1表示打乱样本数据,而每步step跑 batch_size 条样本,一共能跑 data_size num_epochs / batch_size = 5000/50 = 100 步,而每print_iter步报一次loss,那么一共报 100/print_iter = 10次loss。 在代码中还有个参数为 epoch_iter,为保存模型而设置,意为每epoch_iter步时,保存一次模型文件。epoch_iter = data_size / batch_size = 1000/50 = 20,共100步,即跑完train后共保存 100/epoch_iter =100/20 =5个模型文件。若shuffle = 0,epoch_iter 意为跑完一次原data_size 需要的步数,若shuffle = 1,epoch_iter 意为跑完一次与原data_size一样大的数据集需要的步数,这样的话,最终保存的模型数量 = num_epochs 。 假设测试集的 data_size = 200时,运行模型预测predict任务时,经常设置num_epochs = 1,因为在测试时没必要重复测试样本,是否打乱数据影响也不大,当设置batch_size = 50,测试数据总样本量 = data_size *num_epochs =200仍然是原测试数据集,每步跑batch_size = 50条数据,共200/50=4steps跑完,最终会打印一个accuracy数值。 这些参数只对模型训练有效,模型预测的predict任务里的这些参数需要在tf_simnet.py内的predict函数内修改!!!(为自己修改代码把配置参数提到配置文件里埋下伏笔😂) 其它说明: 保存模型文件的路径需要自己手动添加,在目录上新建model和pointwise文件夹: 12345678910111213simnet |-tf |- date |- examples |- layers |- losses |- nets |- tools |- util # 新建下面的文件夹 |- model |- pointwise .sh 任务文件走读数据准备完毕,配置文件修改完成后,可在Linux执行.sh脚本文件来实现 train / predict / freeze / …等任务。 run_train.sh通过执行脚本run_train.sh可以启动训练任务,打开run_train.sh: 123456789101112131415161718192021222324set -e # set -o errexitset -u # set -o nounsetset -o pipefail # 以下命令用来做训练集和测试集数据转换,转换一次形成convert文件便可以,不需要重复转换#-----------------------------------------------------------------------# 将train_pointwise_data转成convert_train_pointwise_dataecho "convert train data"python ./tools/tf_record_writer.py pointwise ./data/train_pointwise_data ./data/convert_train_pointwise_data 0 32#-----------------------------------------------------------------------# 将test_pointwise_data转成convert_test_pointwise_dataecho "convert test data"python ./tools/tf_record_writer.py pointwise ./data/test_pointwise_data ./data/convert_test_pointwise_data 0 32echo "convert data finish"#-----------------------------------------------------------------------in_task_type='train' # 输入任务类型,可选择 train/predict/freeze/convertin_task_conf='./examples/cnn-pointwise.json' # 输入配置文件地址#-----------------------------------------------------------------------# 以下命令运行tf_simnet.py文件,执行train任务,深度语义匹配使用为cnn网络python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf 也可以通过如下方式启动自定义训练,效果与上面的.sh文件是一样的: 123python tf_simnet.py --task train --task_conf examples/cnn_pointwise.json 执行完run_train.sh后,在model文件夹内会自动保存各个epoch_iter和final的模型文件。 run_infer.sh通过执行脚本run_infer.sh可以启动预测任务,可以得到模型预测结果或得分,打开.sh文件: 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail in_task_type='predict' # 选择了predict任务in_task_conf='./examples/cnn-pointwise.json' # 仍然是cnn配置文件路径python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf 也可以通过如下方式启动自定义训练: 123python tf_simnet.py --task predict --task_conf examples/cnn_pointwise.json 执行完run_infer.sh之后,会自动在路径上生成result文件,可打开查看。 自定义.sh任务文件据观察,.sh文件主要运行tf_simnet.py文件,有两个参数可以自定义。 参数说明: task: 任务类型 ,可选择 train/predict/freeze/convert 。 task_conf: 使用配置文件地址 接下来尝试生成自定义的.sh任务文件: 可以把转换数据的命令抽取出来,形成 run_convert_data.sh文件: 执行完run_convert_data.sh后,在data文件夹里会自动生成convert前缀的数据文件。 12345678910set -e # set -o errexitset -u # set -o nounsetset -o pipefail # 将具体的文件名把下面命令里的中文字替换掉即可echo "convert train data"python ./tools/tf_record_writer.py pointwise ./data/待转化的训练数据文件名 ./data/已转化的训练数据文件名 0 32echo "convert test data"python ./tools/tf_record_writer.py pointwise ./data/待转化的测试数据文件名 ./data/已转化的测试数据文件名 0 32echo "convert data finish" 定义一个cnn配置文件的执行freeze任务的run_freeze_cnn.sh: 执行完run_freeze_cnn.sh后,根据配置文件里“graph”的参数设置,在路径上会自动生成graph文件夹,里面有model_cnn_pairwise.protxt文件。 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail in_task_type='freeze' # 输入任务类型,可选择 train/predict/freeze/convertin_task_conf='./examples/cnn-pointwise.json'python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf 定义一个使用lstm网络的训练任务 run_train_lstm.sh,同时一定要记得修改配置文件!!: 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail in_task_type='train'in_task_conf='./examples/lstm-pointwise.json' # 修改了配置文件路径,配置文件内参数也得修改好python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf 当lstm的train任务跑完后,利用其保存的模型文件进行predict任务,新建run_predict_lstm.sh: 123456789set -e # set -o errexitset -u # set -o nounsetset -o pipefail in_task_type='predict' # 修改了任务类型in_task_conf='./examples/lstm-pointwise.json' # 确认lstm配置文件路径python tf_simnet.py \ --task $in_task_type \ --task_conf $in_task_conf .py 文件走读tf_simnet.pytf_simnet.py是整个深度语义匹配框架的主运行py文件,打开如下,已对主要代码进行一行行的注释: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187#coding=utf-8# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.# # Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at# # http://www.apache.org/licenses/LICENSE-2.0# # Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.import argparseimport loggingimport jsonimport sysimport osimport tensorflow as tffrom utils import datafeedsfrom utils import controlerfrom utils import utilityfrom utils import converter_WORK_DIR = os.path.split(os.path.realpath(__file__))[0]sys.path.append(os.path.join(_WORK_DIR, '../../../common'))import log# 加载配置文件信息,形成conf配置字典def load_config(config_file): """ load config """ with open(config_file, "r") as f: try: conf = json.load(f) except Exception: logging.error("load json file %s error" % config_file) conf_dict = {} unused = [conf_dict.update(conf[k]) for k in conf] logging.debug("\n".join( ["%s=%s" % (u, conf_dict[u]) for u in conf_dict])) return conf_dictdef train(conf_dict): """ train """ # 根据配置文件先判断data文件是 pairwise 还是 pointwise training_mode = conf_dict["training_mode"] # 根据配置文件输入net网络文件路径 + 网络类型class net = utility.import_object( conf_dict["net_py"], conf_dict["net_class"])(conf_dict) if training_mode == "pointwise": # 喂数据,从train_file, batch_size, num_epochs, shuffle等配置信息确定数据队列长度和秩序 datafeed = datafeeds.TFPointwisePaddingData(conf_dict) # 从转换后的数据中拿出一组组 input_l, input_r, label_y, 一步步的拿进来训练 input_l, input_r, label_y = datafeed.ops() # 做语义匹配预测 pred = net.predict(input_l, input_r) # 根据配置文件设置loss函数路径 + loss种类 loss_layer = utility.import_object( conf_dict["loss_py"], conf_dict["loss_class"])() loss = loss_layer.ops(pred, label_y) # pairwise 先忽略 elif training_mode == "pairwise": datafeed = datafeeds.TFPairwisePaddingData(conf_dict) input_l, input_r, neg_input = datafeed.ops() pos_score = net.predict(input_l, input_r) neg_score = net.predict(input_l, neg_input) loss_layer = utility.import_object( conf_dict["loss_py"], conf_dict["loss_class"])(conf_dict) loss = loss_layer.ops(pos_score, neg_score) else: print >> sys.stderr, "training mode not supported" sys.exit(1) # -------------------- # define optimizer # -------------------- # 超参数 学习速率的设置 lr = float(conf_dict["learning_rate"]) optimizer = tf.train.AdamOptimizer(learning_rate=lr).minimize(loss) # 运行 controler 的 run_trainer 函数 controler.run_trainer(loss, optimizer, conf_dict)def predict(conf_dict): """ predict """ # 根据配置文件输入net网络文件路径 + 网络类型class net = utility.import_object( conf_dict["net_py"], conf_dict["net_class"])(conf_dict) # 更新/覆盖 conf_dict配置文件 的配置参数 (这里需要手动调整) conf_dict.update({"num_epochs": "1", "batch_size": "1", "shuffle": "0", "train_file": conf_dict["test_file"]}) # num_epochs = 1,数据集为样本全量的1倍,仍为原测试样本 # batch_size = 1,一次只读一条样本,覆盖掉配置文件batch_size的值 # shuffle = 0 /1,样本数据是否随机读取,覆盖掉配置文件shuffle的值 # train_file 为 测试集样本路径,覆盖掉配置文件的训练集data路径 # 喂数据,从train_file, batch_size, num_epochs, shuffle等配置信息确定数据队列长度和秩序 test_datafeed = datafeeds.TFPointwisePaddingData(conf_dict) # 从转换后的数据中拿出 test_l, test_r, test_y test_l, test_r, test_y = test_datafeed.ops() # test network # 做语义匹配预测 pred = net.predict(test_l, test_r) # 运行 controler 的 run_predict 函数 controler.run_predict(pred, test_y, conf_dict) # run_predict(pred, label, config)def freeze(conf_dict): """ freeze net for c api predict """ # 网络文件路径 + 网络类型class net = utility.import_object( conf_dict["net_py"], conf_dict["net_class"])(conf_dict) test_l = dict([(u, tf.placeholder(tf.int32, [None, v], name=u)) for (u, v) in dict(conf_dict["left_slots"]).iteritems()]) test_r = dict([(u, tf.placeholder(tf.int32, [None, v], name=u)) for (u, v) in dict(conf_dict["right_slots"]).iteritems()]) pred = net.predict(test_l, test_r) controler.graph_save(pred, conf_dict)def convert(conf_dict): """ convert """ converter.run_convert(conf_dict)if __name__ == "__main__": # 在tf目录下自动新增log文件夹 log.init_log("./log/tensorflow") # 命令解析 parser = argparse.ArgumentParser() # 增加task命令: 命令选项为 train // predict // freeze // convert parser.add_argument('--task', default='train', help='task: train/predict/freeze/convert, the default value is train.') # 增加task_conf命令:命令选项为examples目录下的json文件 parser.add_argument('--task_conf', default='./examples/cnn-pointwise.json', help='task_conf: config file for this task') args = parser.parse_args() task_conf = args.task_conf # 加载配置文件,config config = load_config(task_conf) task = args.task # 判断任务类型 if args.task == 'train': train(config) elif args.task == 'predict': predict(config) elif args.task == 'freeze': freeze(config) elif args.task == 'convert': convert(config) else: print >> sys.stderr, 'task type error.' 可直接先看最下面的if __name__ == "__main__":,两个parser.add_argument分别对应.sh文件的两个task和task_conf参数。选择好任务和配置文件后就运行任务,假如 args.task == ‘train’,那么运行train函数。 再跳到def train(conf_dict):这行,看train函数如何运行。前面几行还是比较好理解,如有疑问可发私信问我,得到loss,optimizer和配置conf_dict之后,最后一行又运行了一个大函数: 12# 运行 controler 的 run_trainer 函数controler.run_trainer(loss, optimizer, conf_dict) 那么就再跳到controler这个文件,继续往下看。 需要特殊说明的是,当模型预测执行predict任务运行predict函数时,num_epochs/batch_size/shuffle/…等参数需要在代码里面调整,而配置文件里的参数设置对模型预测不起作用,在tf_simnet.py的predict函数找到如下代码进行设置: 1234567# 更新测试集的 conf_dict配置文件 的配置参数 (这里需要手动调整) conf_dict.update({"num_epochs": "1", "batch_size": "1", "shuffle": "0", "train_file": conf_dict["test_file"]}) # num_epochs = 1,数据集为样本全量的1倍,仍为原测试样本 # batch_size = 1,一次只读一条样本,覆盖掉原有batch_size的值 # shuffle = 0 /1,样本数据是否随机读取,覆盖掉原有shuffle的值 # train_file 为 测试集样本路径,覆盖掉原先的训练集data路径 controler.py打开controler.py可以看见,里面有与tf_simnet.py里的train/predict/freeze函数一一对应的run_train/run_predict/graph_save函数,对重要代码已做注释: 继续上面的思路,直接看run_trainer(loss, optimizer, conf_dict)函数。同样,如有疑问可留言或私信我。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162#coding=utf-8# Copyright (c) 2018 Baidu, Inc. All Rights Reserved.# # Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at# # http://www.apache.org/licenses/LICENSE-2.0# # Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.import sysimport tensorflow as tfdef run_predict(pred, label, config): """ run classification predict function handle """ mean_acc = 0.0 # 执行模型保存任务,需要创建一个Saver对象 saver = tf.train.Saver() mode = config["training_mode"] # label输入值为二维的0/1向量,类似将原先的label值onehot化,pred同理为二维向量 # label = pred = 形如([0,1],[1,0],[1,0],[0,1],...) # 找出数值为1/或者说数值最大的index值,因为是2维,index取值在[0,1]之间,(,1)代表行(,0)代表列 label_index = tf.argmax(label, 1) # label_index为向量,形如([1],[0],[0],[1],...) if mode == "pointwise": pred_prob = tf.nn.softmax(pred, -1) # 向量, 将pred变成二分类的概率小数向量,形如([0.73,0.27],[0.1,0.9],[1.0,0],[0.87,0.13],...) score = tf.reduce_max(pred_prob, -1) # 向量, 取向量里的最大概率值,而pred结果为大概率值的输出,(,-1)为去掉最后一个向量,形如([0.73],[0.9],[1.0],[0.87],...) pred_index = tf.argmax(pred_prob, 1) # 向量, 同理label_index,形如([0],[1],[0],[0],...) correct_pred = tf.equal(pred_index, label_index) # 向量, 判断label_index与pred_index是否一致,一致为1,不一致为0 acc = tf.reduce_mean(tf.cast(correct_pred, "float")) # 数字,array([0,0,0,1,1])与array([1,0,1,0,1]), 求得correct_pred = 2,acc=2/5= 0.4=求猜对的期望值 # 'pairwise'先忽略 elif mode == "pairwise": score = pred pred_index = tf.argmax(pred, 1) acc = tf.constant([0.0]) # 找到模型文件路径 modelfile = config["test_model_file"] #新建result文件,保存预测结果 result_file = file(config["test_result"], "w") step = 0 init = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer()) with tf.Session(config=tf.ConfigProto(intra_op_parallelism_threads=1)) \ as sess: sess.run(init) #初始化设置 saver.restore(sess, modelfile) # 加载模型文件 coord = tf.train.Coordinator() read_thread = tf.train.start_queue_runners(sess=sess, coord=coord) while not coord.should_stop(): step += 1 try: ground, pi, a, prob = sess.run([label_index, pred_index, acc, score]) # 用二维向量来表示label和pred,用数值1在二维向量中的位置是否一致判断label与pred结果是否一致 mean_acc += a for i in range(len(prob)): result_file.write("%d\t%d\t%f\n" % (ground[i], pi[i], prob[i])) # 一步步写result文件,共3列,分别是ground[i], pi[i], prob[i] # ground[i]是测试集label的值,0或1 # pi[i]是预测的值,0或1 # prob[i]是预测结果的概率 except tf.errors.OutOfRangeError: coord.request_stop() coord.join(read_thread) sess.close() result_file.close() if mode == "pointwise": mean_acc = mean_acc / step print >> sys.stderr, "accuracy: %4.2f" % (mean_acc * 100) # 输出accuracydef run_trainer(loss, optimizer, config): """ run classification training function handle """ thread_num = int(config["thread_num"]) model_path = config["model_path"] # 模型文件的存储路径 model_file = config["model_prefix"] # 模型文件名的前缀 print_iter = int(config["print_iter"]) data_size = int(config["data_size"]) batch_size = int(config["batch_size"]) epoch_iter = int(data_size / batch_size) avg_cost = 0.0 # 执行模型保存任务,需要创建一个Saver对象 saver = tf.train.Saver(max_to_keep=None) # 初始化 global variables init = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer()) with tf.Session(config=tf.ConfigProto(intra_op_parallelism_threads=thread_num, inter_op_parallelism_threads=thread_num)) \ as sess: sess.run(init) # 创建一个线程管理器(协调器)对象 coord = tf.train.Coordinator() # 把tensor推入内存序列中,供计算单元调用 read_thread = tf.train.start_queue_runners(sess=sess, coord=coord) # 启动入队线程,由多个或单个线程 step = 0 epoch_num = 1 while not coord.should_stop(): # coord.should_stop() 查询是否应该终止所有线程 try: step += 1 c, _= sess.run([loss, optimizer]) avg_cost += c #----------------------------------------------------------- # 当step步数是print_iter的倍数时,即每print_iter步时,打印一个loss if step % print_iter == 0: print("loss: %f" % ((avg_cost / print_iter))) avg_cost = 0.0 #----------------------------------------------------------- # 当step步数是epoch_iter的倍数时,即每epoch_iter步时,保存一个model文件 if step % epoch_iter == 0: print("save model epoch%d" % (epoch_num)) save_path = saver.save(sess, "%s/%s.epoch%d" % (model_path, model_file, epoch_num)) epoch_num += 1 except tf.errors.OutOfRangeError: # 全部步数走完后,保存一个final的模型文件 save_path = saver.save(sess, "%s/%s.final" % (model_path, model_file)) coord.request_stop() # 发出终止所有线程的命令 coord.join(read_thread) # 把线程加入主线程,等待threads结束 sess.close()def graph_save(pred, config): """ run classify predict """ graph_path=config["graph_path"] graph_name=config["graph_name"] mode = config["training_mode"] if mode == "pointwise": pred_prob = tf.nn.softmax(pred, -1, name="output_prob") elif mode == "pairwise": pred_prob = tf.identity(pred, name="output_prob") saver = tf.train.Saver() step = 0 init = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer()) with tf.Session(config=tf.ConfigProto(intra_op_parallelism_threads=1)) \ as sess: sess.run(init) tf.train.write_graph(sess.graph_def, graph_path, graph_name, as_text=True) sess.close() 跑完run_trainer(loss, optimizer, config),就自动保存了模型文件,供模型预测使用。 同理按照这个思路,回到tf_simnet.py可以看predict任务或者freeze(graph)任务的代码流程。同样也会再看到controler.py里面的函数是如何执行任务的。 写在最后 不同的.sh文件有不同的task和task_conf参数值,对应的是运行tf_simnet.py和controler.py里不同的函数。 其它的.py文件点开来应该不太难理解,如有疑问可留言或私信我。 每个模型文件比较大,我跑出来的每个有1.5G左右大小。 若想观察每一步的数据形状或者变换情况,可自行加入print命令打印出来看看。 每个任务完成后有自动生成的文件,可以研究看看,比如model文件,result文件,graph文件,log文件。 代码走读只是理解的第一步,若要扎实掌握还需自己改动代码,再调试调试。 欢迎打赏😘]]></content>
<tags>
<tag>Semantic Matching</tag>
<tag>NLP</tag>
<tag>code review</tag>
<tag>QA</tag>
<tag>语义匹配</tag>
</tags>
</entry>
<entry>
<title><![CDATA[NLP笔记 - Word Tokenization // wordcloud 词云图教程]]></title>
<url>%2F2018%2F09%2F03%2FNLP%E7%AC%94%E8%AE%B0-Word-Tokenization-wordcloud%2F</url>
<content type="text"><![CDATA[面向对象:想做简单的文本可视化分析 选手。 概念解释 词云图(wordcloud):是这两年比较火的文本可视化分析的一种,上图就知道说的啥了: jieba:python库,用于中文分词。 wordcloud:python库,用于词云图制作。 停用词表(stopwords):在英文中像“the / of / a / for /…”,在中文中像“的 / 是 / 也 / 之 /…”这样的没有实际意义却出现频率较高的词。为了防止这些词抢了比如故事主角名的位置,就事先作为停用词,不进入文本分析。 按规矩,先上文档结构图!文档中所需文件的下载地址,点这里 123456789101112|-wordcloud //新建文件夹 |- data //新建文件夹 |- alice.txt //下载文件 |- yxgltext.txt //下载文件 |- stopwords.txt //下载文件 |- SourceHanSerifK-Light.otf //下载文件 |- plot //新建文件夹 |- alice_color.png //下载图片 |- queen.jpg //下载图片 |- alice1.py //新建python文件 |- alice2.py //新建python文件 |- queen.py //新建python文件 英文词云图例子1:10行代码搞定的词云图 // alice1.py输入: 123456789101112131415161718192021222324252627# -*- coding: utf-8 -*-"""Created on Mon Sep 3 17:53:03 2018@author: Yi"""import osos.chdir("C:/Users/Yi/Desktop/nlp/wordcloud") # 换成你的wordcloud文件夹所在路径from wordcloud import WordCloudf = open('data/alice.txt').read()wordcloud = WordCloud(background_color="white",width=1000, height=860, margin=2).generate(f) # width,height,margin可以设置图片属性# generate 可以对全部文本进行自动分词,但是对中文支持不好# wordcloud = WordCloud(font_path = r'D:\Fonts\simkai.ttf').generate(f)# 你可以通过font_path参数来设置字体集# background_color参数为设置背景颜色,默认颜色为黑色import matplotlib.pyplot as pltax = plt.imshow(wordcloud)fig = ax.figurefig.set_size_inches(25,20) # 可调节图片紧密 尺寸程度plt.axis("off")plt.show()wordcloud.to_file('plot/test.png') 输出: 例子2:有形状的词云图 // alice2.py输入: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970# -*- coding: utf-8 -*-"""Created on Tue Sep 4 10:05:29 2018@author: Yi"""import osos.chdir("C:/Users/Yi/Desktop/nlp/wordcloud")from PIL import Imageimport numpy as npimport matplotlib.pyplot as pltfrom wordcloud import WordCloud, STOPWORDS, ImageColorGenerator# Read the whole text.text = open('data/alice.txt').read()alice_coloring = np.array(Image.open("plot/alice_color.png")) # 可随意更换图片stopwords = set(STOPWORDS)stopwords.add("said")# 你可以通过 mask 参数 来设置词云形状wc = WordCloud(background_color="white", max_words=2000, mask=alice_coloring, stopwords=stopwords, max_font_size=40, random_state=42)# generate word cloudwc.generate(text)# create coloring from imageimage_colors = ImageColorGenerator(alice_coloring)# 方式 1 -------------------------------------------------------------------# show#fig, axes = plt.subplots(1, 3)#axes[0].imshow(wc, interpolation="bilinear")#axes[1].imshow(wc.recolor(color_func=image_colors), interpolation="bilinear")#axes[2].imshow(alice_coloring, cmap=plt.cm.gray, interpolation="bilinear")##for ax in axes:# ax.set_axis_off()# fig = ax.figure# fig.set_size_inches(25,20) # 可调节图片紧密 尺寸程度#plt.show()# 方式 2 -------------------------------------------------------------------# show# 在只设置mask的情况下,你将会得到一个拥有图片形状的词云plt.axis("off")ax = plt.imshow(wc, interpolation="bilinear")fig = ax.figurefig.set_size_inches(25,20) # 可调节图片紧密 尺寸程度plt.figure()# recolor wordcloud and show# we could also give color_func=image_colors directly in the constructor# 我们还可以直接在构造函数中直接给颜色# 通过这种方式词云将会按照给定的图片颜色布局生成字体颜色策略plt.axis("off")ax = plt.imshow(wc.recolor(color_func=image_colors), interpolation="bilinear")fig = ax.figurefig.set_size_inches(25,20) # 可调节图片紧密 尺寸程度plt.figure()plt.axis("off")ax = plt.imshow(alice_coloring, cmap=plt.cm.gray, interpolation="bilinear")fig = ax.figurefig.set_size_inches(25,20) # 可调节图片紧密 尺寸程度plt.show() 输出: 中文词云图例子:延禧攻略的白月光 // queen.py 中文与英文还是有点不一样的,在停用词表就需要自己弄一套等等。记得跑之前要把该下载的文件下载到文件夹里。 输入: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354# -*- coding: utf-8 -*-"""Created on Mon Sep 3 18:27:38 2018@author: Yi"""import osos.chdir("C:/Users/Yi/Desktop/nlp/wordcloud") import jieba.analyse # 导入结巴分词import numpy as np # numpyfrom wordcloud import WordCloud, STOPWORDS # 词云工具和自带的的停用词,英文from PIL import Image # 图片处理import matplotlib.pyplot as pltdef handle(textfile, stopword): with open(textfile, 'r') as f: data = f.read() wordlist = jieba.analyse.extract_tags(data, topK=100) # 分词,取前100 wordStr = " ".join(wordlist) print (wordStr) hand = np.array(Image.open('plot/queen.jpg')) # 打开一张图片,词语以图片形状为背景分布 my_cloudword = WordCloud( # wordcloud参数配置 width=1024, height=768, background_color = 'white', # 背景颜色设置白色 mask = hand, # 背景图片 max_words = 100, # 最大显示的字数 stopwords = stopword, # 停用词 max_font_size = 100, # 字体最大值 font_path='data/SourceHanSerifK-Light.otf', # 设置中文字体,若是有中文的话,这句代码必须添加,不然会出现方框,不出现汉字 random_state=3, # 设置有多少种随机生成状态,即有多少种配色方案 ) my_cloudword.generate(wordStr) # 生成图片 my_cloudword.to_file('plot/queen.png') # 保存 plt.axis('off') # 是否显示x轴、y轴下标 ax = plt.imshow(my_cloudword) # 显示词云图 fig = ax.figure fig.set_size_inches(25,20) # 可调节图片紧密 尺寸程度 plt.show() # 显示stopwords = open('data/stopwords.txt').read()stopwords = set(stopwords.split('\n'))if __name__ == '__main__': handle('data/yxgl.txt', stopwords) 输出: 再来一次: 白月光皇后的人头形状和扇子形状都还在的。 写在最后这里用到的文本分析的技术只停留在分词阶段,还是比较简单的。可视化分析永远是最吸引人的。快去试一下吧~😋]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>NLP</tag>
<tag>jieba</tag>
<tag>tokenization</tag>
<tag>wordcloud</tag>
</tags>
</entry>
<entry>
<title><![CDATA[NLP笔记 - Word Embedding // doc2vec 之 延禧攻略]]></title>
<url>%2F2018%2F08%2F28%2FNLP%E7%AC%94%E8%AE%B0-Word-Embedding-doc2vec%2F</url>
<content type="text"><![CDATA[面向读者:nlp入门,python选手,对word embedding(词嵌入)有大概了解。本文是基于doc2vec的一个关于延禧攻略剧情文本的小demo。doc2vev基于word2vec,它俩很像,使用方法也很像。有空再把原理补上。 语料文本yxgltext.txt点这里下载,其实就是从百度上复制粘贴的前20集左右的剧情文字,大家可以随意更改语料文字。文件结构如下,记得下载yxgltext.txt。 12345|-nlp //新建文件夹 |- doc2vec.py //新建python文件 |- data //新建文件夹 |- yxgltext.txt //下载语料数据放在data文件夹目录下 |- model //新建文件夹 Talk is cheap, show me the code! 上代码~~ 输入: 1234567891011121314import osos.chdir("C:/Users/Yi/Desktop/nlp") # nlp文件夹的路径import jieba # 中文分词工具import sysimport gensimimport sklearnimport numpy as npfrom gensim.models.doc2vec import Doc2Vec, LabeledSentence #从gensim导入doc2vecTaggededDocument = gensim.models.doc2vec.TaggedDocument# 虚词,可以随意添加删除stoplist = ['的','了','被','。',',','、','她','自己','他','并','和','都','去','\n'] 输入: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051#进行中文分词def cut_files(): filePath = 'data/yxgltext.txt' fr = open(filePath, 'rb') fvideo = open('data/yxglCut.txt', "w") for line in fr.readlines(): curLine =' '.join(list(jieba.cut(line))) fvideo.writelines(curLine) #读取分词后的数据并打标记,放到x_train供后续索引,占用很大内存(供小数据量使用)def get_datasest(): with open("data/yxglCut.txt", 'r') as cf: docs = cf.readlines() # 删除常用词 for idx in list(range(0,len(docs))): docs[idx] = ' '.join([word for word in docs[idx].split( ) if word not in stoplist]) docs = [doc for doc in docs if len(doc)>0] print(len(docs)) x_train = [] for i, text in enumerate(docs): word_list = text.split(' ') l = len(word_list) word_list[l - 1] = word_list[l - 1].strip() document = TaggededDocument(word_list, tags=[i]) x_train.append(document) return x_train#模型训练def train(x_train, size=200, epoch_num=1): # size=200 意味着 每个词向量是200维的 # 使用 Doc2Vec 建模 model_dm = Doc2Vec(x_train, min_count=1, window=3, size=size, sample=1e-3, negative=5, workers=4) #model_dm.train(x_train, total_examples=model_dm.corpus_count, epochs=70) model_dm.save('model/model_dm_doc2vec') return model_dm#实例def test():# model_dm = Doc2Vec.load("model/model_dm_doc2vec") test_text = ['我', '喜欢', '傅恒'] inferred_vector_dm = model_dm.infer_vector(test_text) # 选取相关度最高的10个词 sims = model_dm.docvecs.most_similar([inferred_vector_dm], topn=10) return sims 输入: 12345678910cut_files()x_train=get_datasest()model_dm = train(x_train)sims = test()for count, sim in sims: sentence = x_train[count] words = '' for word in sentence[0]: words = words + word + ' ' print (words, sim, len(sentence[0])) 输出: 123娴妃 提议 让 从 江南 请来 名医 叶天士 为 五 阿哥 医治 皇上 应允 叶天士 肯定 五 阿哥 患 黄疸 保证 用退 黄方 就 可治好 弘历 松 口气 高 贵妃 见风使舵 向 皇上 告罪 皇上 表示 谅解 这时 纯妃 带 人 抬 此前 照料 愉 贵人 饮食 一名 蒙古 厨师 尸体 上来 了解 过愉 贵人 孕期 饮食习惯 后 叶天士 禀报 弘历 婴儿 瞳孔 金黄 怪病 多因 母体 湿热 胆汁 淤积 而生 孕妇 应当 注意 饮食 不过 分 食用 甜食 烫食 腥膻 之物 璎珞 意有 所指 是 高 贵妃 想 对付 愉 贵人 五 阿哥 高 贵妃 辩驳 纯妃 却 呈 上 证据 是 厨师 死前 留下 一封 指认 高 贵妃 血书 弘历 大为 恼火 软禁 高 贵妃 皇上 准备 离开 时 明玉 拦住 皇上 告发 璎珞 盗用 皇后 金印 璎珞 打开 匣子 里面 只是 一块 砚台 明玉因 诬告 而 受罚 随后 璎珞 拦住 纯妃 与 说话 指出 厨师 自尽 留下 血书 一事 是 策划 纯妃 提醒 璎珞 别站 错 队 0.17824654281139374 160高 贵妃 巧妙 利用 这次 机会 对 皇上 诉说 衷肠 赢得 弘历 谅解 与此同时 皇后 在 长春 宫门 口苦 等 弘历 不至 深感 失望 次日 璎珞 在 御花园 发泄 心中 对 皇上 辜负 皇后 不满 遇到 傅恒 璎珞 向 替 皇后 鸣不平 出言不逊 傅恒 劝阻 随即 璎珞 因 百般 查探 姐姐 死因 却 一无所获 越发 焦躁 弘历 深夜 批阅 奏章 身体 不适 召 太医 前来 诊治 发现 患 疥疮 皇后 不顾 传染 危险 执意 要 搬入 养心殿 亲自 照料 弘历 原本 让 明玉 随行 可明玉 却 将 此 差 推 给 璎珞 璎珞 为了 调查 皇上 身边 亲信 查探 姐姐 真正 死因 于是 同意 跟 明玉 调换 差事 跟随 皇后 一道 搬 养心殿 璎珞 替 皇上 上药 皇上 十分 反感 拒绝 璎珞 可 李玉 粗手笨脚 弄 痛 皇上 皇上 恼怒 璎珞 告诉 皇上 养心殿 伺候 多半 是 太监 如果 坚持 那么 只好 请 皇后 来 替 上药 皇上 无奈 只好 让 璎珞 继续 皇上 涂 药 之后 依然 燥热 瘙痒 难耐 皇后 衣不解带 地 照顾 整整 一 晚上 璎珞 见状 十分 动容 璎珞 试探 李玉 询问 乾 清宫 夜宴 当晚 曾经 离席 宗室 可惜 一无所获 0.1443883627653122 185... 输入: 1print(model_dm.wv['璎珞']) 输出’璎珞’的200维词向量: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950[ 1.32339250e-03 -4.36101720e-04 8.61682580e-04 -5.60876098e-04 -1.10074517e-03 -4.01582598e-04 1.39182212e-05 1.03741838e-03 1.33155310e-03 4.53286630e-04 -1.02781062e-03 -8.92800104e-04 1.19402306e-03 -3.00986052e-04 -1.55415002e-03 -2.69316044e-03 1.58681255e-03 -8.10362690e-04 5.34354069e-04 -1.31634891e-03 -3.59648140e-03 2.49065284e-04 8.13953171e-04 -8.55766921e-05 2.76492530e-04 -1.29517284e-03 1.02521526e-03 8.73336976e-04 1.62727723e-03 -6.10298535e-04 -1.21042994e-03 -1.87295862e-03 -2.03051459e-04 -3.54788470e-04 1.25130301e-03 8.69541487e-04 -2.45160703e-03 -9.03088134e-04 5.02681173e-03 -1.03742653e-03 3.97383585e-04 1.10275706e-03 3.76813230e-04 -2.43625650e-03 3.11101991e-04 1.97053305e-03 2.52972008e-03 -1.45180838e-03 -1.74685894e-03 1.52873830e-03 4.81644034e-04 1.05112646e-04 2.67350441e-03 8.58452288e-04 6.63276296e-05 -1.97039312e-03 5.31882746e-04 -4.36584116e-04 1.26765005e-03 -3.08679766e-03 1.69386994e-03 -2.96112709e-03 2.48387340e-03 -3.73846688e-03 -3.07446043e-03 -4.49631305e-04 1.78120867e-03 -1.19638827e-03 -2.00018892e-03 -6.16657664e-04 1.24890637e-03 1.04953512e-03 6.38565107e-04 -8.65224341e-04 1.56678446e-03 2.29814858e-03 -4.69850667e-04 6.30659808e-04 2.44404143e-03 1.34824484e-03 -3.52538045e-04 2.64616770e-04 9.84614133e-04 -5.64393296e-04 -1.46174955e-03 2.11890996e-03 2.74263322e-04 -1.95100205e-03 2.42348132e-03 -4.13818937e-03 1.28919329e-03 -7.49823987e-04 3.59561713e-03 2.89021351e-04 1.64465397e-04 3.35634919e-04 6.11493131e-04 2.10861443e-03 6.76521973e-04 -1.72132370e-03 -9.39077465e-04 1.75529323e-03 -1.22920389e-03 2.14341236e-03 -2.19211495e-03 1.65924046e-03 2.23257625e-03 -2.71887379e-03 -3.23694688e-03 -2.48166034e-03 -3.01317009e-03 1.18382962e-03 3.18966959e-05 3.01953492e-04 2.36387877e-03 5.23283597e-05 1.89765415e-03 8.61766574e-04 -2.39132158e-03 -1.02647720e-03 -1.90407838e-04 5.11635910e-04 1.44841790e-03 2.69743241e-03 1.57171465e-03 -7.98581314e-05 -3.73520626e-04 2.92094832e-04 -7.90165941e-05 -1.03529333e-03 3.86003614e-03 2.65925983e-03 -9.42493731e-04 -2.91984412e-03 -8.32679973e-04 -6.22316380e-04 1.62830914e-03 1.41070038e-03 -1.05310581e-03 -4.29132691e-04 -3.38748004e-03 -2.14482704e-03 2.66522495e-03 -1.70672731e-03 2.21871235e-03 7.67852471e-04 -4.05522675e-04 3.69134732e-03 -2.68788106e-04 8.00681883e-04 1.98179367e-03 -1.21154217e-03 -7.56838883e-04 -9.01334104e-04 -2.56626052e-03 4.35368915e-04 7.19753269e-04 -2.40792311e-03 7.30484782e-04 -1.04375300e-04 1.82642520e-03 -1.83782264e-04 -2.16018991e-03 -1.67128816e-03 -3.14951874e-03 -1.74462073e-03 -3.66404653e-04 1.16418314e-03 2.36262940e-03 -7.21087854e-04 2.59639206e-03 -1.85696199e-03 7.52747059e-04 -1.90908764e-03 -2.16792268e-03 -2.83251936e-03 -1.03030400e-03 3.27490713e-03 4.00006247e-04 3.08081927e-03 -1.79204450e-03 1.68617186e-03 9.10512696e-04 1.23125815e-03 -1.02122920e-03 4.01859492e-04 -3.32432962e-03 9.13784548e-04 -2.05583894e-03 -2.35229125e-03 -8.21198220e-04 -6.70439913e-04 -1.70158059e-03 3.93540040e-03 1.72487774e-03 1.93191075e-03 2.05451762e-03 3.47349187e-03 -2.65299017e-03 -3.04736476e-03] 输入: 12#可以用句向量模型直接根据词向量查询相似度print (model_dm.wv.most_similar('璎珞')) 输出跟“璎珞”最相关的前10个词,以及相关系数: 12345678910[('庆锡', 0.36982783675193787), ('答应', 0.30098065733909607), ('弘历', 0.29272472858428955), ('贵妃', 0.28584328293800354), ('发现', 0.2655611038208008), ('认定', 0.25713881850242615), ('不是', 0.2567000389099121), ('多多', 0.24867823719978333), ('又', 0.2475070059299469), ('利用', 0.2474854439496994)] 看完与“璎珞”强相关的词后,也可以尝试看看“傅恒”,“皇上”的相关词。(还是心疼傅恒😭) “傅恒”的输出结果是: 12345678910[('谣言', 0.25737541913986206), ('离开', 0.24646247923374176), ('涂', 0.23385187983512878), ('媚惑', 0.2333744615316391), ('是', 0.2153894603252411), ('借', 0.20425426959991455), ('却', 0.20283949375152588), ('璎珞', 0.20118796825408936), ('贵人', 0.19429181516170502), ('公道', 0.1942289024591446)] 语料库是前20集的内容,可以看下在前20集“傅恒”与“璎珞”的相关程度,输入: 1print(model_dm.similarity('璎珞', '傅恒')) 输出: 10.23082738 同理输入看看与富察皇后和大猪蹄子的缘分: 12print(model_dm.similarity('璎珞', '皇后'))print(model_dm.similarity('璎珞', '皇上')) 查询字典的样子,输入: 1print(model_dm.wv.vocab) 输出: 1234567891011121314151617181920212223242526272829303132{...'傅恒所言': <gensim.models.keyedvectors.Vocab at 0x18391f309e8>, '祥瑞': <gensim.models.keyedvectors.Vocab at 0x18391f5b438>, '恒等': <gensim.models.keyedvectors.Vocab at 0x18391f30a58>, '珍惜': <gensim.models.keyedvectors.Vocab at 0x18391f30a90>, '出是': <gensim.models.keyedvectors.Vocab at 0x18391f6eb70>, '置之不理': <gensim.models.keyedvectors.Vocab at 0x18391f5ba90>, '立刻': <gensim.models.keyedvectors.Vocab at 0x18391f30b00>, '自从': <gensim.models.keyedvectors.Vocab at 0x18391f30b38>, '里面': <gensim.models.keyedvectors.Vocab at 0x18391f30ba8>, '发抖': <gensim.models.keyedvectors.Vocab at 0x18391f30be0>, '之巅': <gensim.models.keyedvectors.Vocab at 0x18391f30c18>, '搬': <gensim.models.keyedvectors.Vocab at 0x18391f30c50>, '对尔晴': <gensim.models.keyedvectors.Vocab at 0x18391f5e470>, '心惊': <gensim.models.keyedvectors.Vocab at 0x18391f5e5f8>, '我行我素': <gensim.models.keyedvectors.Vocab at 0x18391f6afd0>, '途中': <gensim.models.keyedvectors.Vocab at 0x18391f30cf8>, '三个': <gensim.models.keyedvectors.Vocab at 0x18391f30d30>, '原来': <gensim.models.keyedvectors.Vocab at 0x18391f30d68>, '保护': <gensim.models.keyedvectors.Vocab at 0x18391f30da0>, '那尔布': <gensim.models.keyedvectors.Vocab at 0x1838cf9b5c0>, '近身': <gensim.models.keyedvectors.Vocab at 0x18391f30e48>, '名医': <gensim.models.keyedvectors.Vocab at 0x1838a970908>, '嫁给': <gensim.models.keyedvectors.Vocab at 0x18391f30eb8>, '盛世': <gensim.models.keyedvectors.Vocab at 0x18391f30ef0>, '坠落': <gensim.models.keyedvectors.Vocab at 0x18391f30f28>, '淤积': <gensim.models.keyedvectors.Vocab at 0x18391f74eb8>, '隐患': <gensim.models.keyedvectors.Vocab at 0x18391f4b470>, '进': <gensim.models.keyedvectors.Vocab at 0x18391f30f98>, '侍奉': <gensim.models.keyedvectors.Vocab at 0x18391f54048>, '迥异': <gensim.models.keyedvectors.Vocab at 0x18391f33080>, ...} 查询字典大小: 1print(len(model_dm.wv.vocab)) 同样,也可以把后面的剧情加进去,看看会发生什么变化😁 其余操作参考链接: models.doc2vec models.word2vec Doc2Vec Tutorial on the Lee Dataset]]></content>
<tags>
<tag>NLP</tag>
<tag>word2vec</tag>
<tag>word embedding</tag>
<tag>doc2vec</tag>
</tags>
</entry>
<entry>
<title><![CDATA[NLP笔记 - Word Embedding // bag of words]]></title>
<url>%2F2018%2F08%2F24%2FNLP%E7%AC%94%E8%AE%B0-Word-Embedding%2F</url>
<content type="text"><![CDATA[面向读者:nlp入门学者,python选手 可能还没做过nlp的项目,就对 word embedding(词嵌入)有所耳闻。深度学习为什么那么火,其中之一是不用怎么操心前期数据清洗。在(深度)语义匹配里,进行embedding(嵌入)是进行深度学习的前一步。 概念解释 语义匹配(semantic matching):根据语义来匹配,看两句话(或者多句话)说的是不是一个意思。比如“我想入门nlp。”和“如何学nlp技术?”可以认为是同一个意思,那么这两句话就匹配成功。传统的方法只是字字匹配(term matching),不会将“入门”和“学习”这两个匹配起来。再加一句“nlp的深度模型有哪些?”,明显和前两句不是一个意思,那么就匹配失败。语义匹配经常用在搜索引擎或像知乎问答上,你提问“如何学nlp技术?”,而“我想入门nlp。”这个已经有人回答过了,存在知识库里,机器需要做的就是把你的问题与已有答案的问题匹配起来,把对应的答案传送给你。 字典(dictionary):像新华字典一样的存在,机器也需要有一个字典来理解文字。一个单词对应一个索引,这个索引index往往是一个序列整数。 语料库(corpora):字典是如何来的,自然是因为有很多很多的文字材料。语料可以是所有莎士比亚写的文章,或者所有维基百科的文章,或者一个特定的人发的推文。 词/句/文本 嵌入(embedding):不要被中文的“嵌入”意思带偏。embedding是一个数学术语,代表的是一个映射关系。比如汉英字典里的中文“钞票”映射到英文就是单词“money”。这项技术把词汇表中的单词或短语映射成由实数构成的向量。在计算机中,一个单词映射到的往往就是它的索引数字。毕竟目前计算机也只能理解数字。 TF-IDF(term frequency–inverse document frequency):TF意思是词频(Term Frequency),IDF意思是逆文本频率指数(Inverse Document Frequency)。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。 跑个小例子在getting started,提起过gensim这个python包。本文就具体讲一下这个包的使用方法。首先pip install gensim,然后打开python3,其它没下载的包请自己手动下载。(jupyter版本链接) 输入: 12import logginglogging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) 1234import osimport tempfileTEMP_FOLDER = tempfile.gettempdir()print('Folder "{}" will be used to save temporary dictionary and corpus.'.format(TEMP_FOLDER)) 下面是一个迷你的语料库,由9个字符串文本组成,每个字符串包含一个句子。语料是指一组文档的集合。这个集合是gensim的输入,gensim会从这个语料中推断出它的结构,主题等。从语料中推断出的隐含结构,可以用来对一个新的文档指定一个主题。 语料库输入: 1234567891011from gensim import corporadocuments = ["Human machine interface for lab abc computer applications", "A survey of user opinion of computer system response time", "The EPS user interface management system", "System and human system engineering testing of EPS", "Relation of user perceived response time to error measurement", "The generation of random binary unordered trees", "The intersection graph of paths in trees", "Graph minors IV Widths of trees and well quasi ordering", "Graph minors A survey"] 首先,做些预处理。 文本进行分词(tokenization) 删去一些常用词/停用词(像for/ a/ of/ the/…这些词) 删去只出现一次的词(防止太稀疏) 输入: 12345678910111213141516# remove common words and tokenizestoplist = set('for a of the and to in'.split())texts = [[word for word in document.lower().split() if word not in stoplist] for document in documents]# remove words that appear only oncefrom collections import defaultdictfrequency = defaultdict(int)for text in texts: for token in text: frequency[token] += 1texts = [[token for token in text if frequency[token] > 1] for text in texts]from pprint import pprint # pretty-printerpprint(texts) 输出: 123456789[['human', 'interface', 'computer'], ['survey', 'user', 'computer', 'system', 'response', 'time'], ['eps', 'user', 'interface', 'system'], ['system', 'human', 'system', 'eps'], ['user', 'response', 'time'], ['trees'], ['graph', 'trees'], ['graph', 'minors', 'trees'], ['graph', 'minors', 'survey']] 预处理的方式可以千变万化,上面只是举个例子。接下来根据上面剩下的单词生成字典,输入: 123dictionary = corpora.Dictionary(texts)dictionary.save('deerwester.dict') # store the dictionary, for future referenceprint(dictionary) 输出: 1Dictionary(12 unique tokens: ['human', 'interface', 'computer', 'survey', 'user']...) 可以看出语料库生成的字典里有12个不同的单词。意味着语料库的每一个文本,也就是每一句话,都可以被12维的稀疏向量表示。 输入: 1print(dictionary.token2id) 输出字典mapping,语料中的每一个单词关联一个唯一的id。字典单词与id能一一对应就行,不同的人跑的id数字可能变化: 1{'human': 0, 'interface': 1, 'computer': 2, 'survey': 3, 'user': 4, 'system': 5, 'response': 6, 'time': 7, 'eps': 8, 'trees': 9, 'graph': 10, 'minors': 11} 如果要对文档的隐含结构进行推断,就需要一种数学上能处理的文档表示方法。一种方法是把每个文档表达为一个向量。有很多种表示方法,一种常见的方法是bag-of-words模型,也叫做“词袋”。在词袋模型中,每篇文档(在这里是每个字符串句子)被表示成一个向量,代表字典中每个词出现的次数。词袋模型的一个重要特点是,它完全忽略的单词在句子中出现的顺序,这也就是“词袋”这个名字的由来。 词袋示例,输入: 123new_doc = "Human computer interaction"new_vec = dictionary.doc2bow(new_doc.lower().split())print(new_vec) # the word "interaction" does not appear in the dictionary and is ignored 输出: 1[(0, 1), (2, 1)] 新样本是一个新句子(注意到这句话并没有出现在原始的预料中):”Human computer interaction” doc2bow()函数生成的元组中,括号左边代表单词id,括号右边代表单词在样例中的出现次数。生成的是一个像[(word_id, word_count), …]的稀疏向量,也就是词袋。 “Human”和“computer”是出现在语料库的,因此也存在在字典里,其id分别是0和2,各自在新样本里出现过一次,因此出现频次都是1。因此(0, 1), (2, 1)分别代表“Human”和“computer”。“interaction”不存在字典里,不在稀疏向量里出现。而其他存在在字典里,却在新句子中出现0次的单词,也不显示在稀疏向量里。也就说明每个小括号右边的数字不会小于1。 因此这个新句子的12维向量最终结果是[(0, 1), (2, 1)]。如果不想出现频次这个特征,可以尝试下doc2idx这个函数,同时按照单词在句子中出现的顺序进行id的显示。 把语料库的句子都转换成稀疏向量,输入: 123corpus = [dictionary.doc2bow(text) for text in texts]corpora.MmCorpus.serialize('deerwester.mm', corpus) # store to disk, for later useprint(corpus) 输出: 123456789[(0, 1), (1, 1), (2, 1)][(2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)][(1, 1), (4, 1), (5, 1), (8, 1)][(0, 1), (5, 2), (8, 1)][(4, 1), (6, 1), (7, 1)][(9, 1)][(9, 1), (10, 1)][(9, 1), (10, 1), (11, 1)][(3, 1), (10, 1), (11, 1)] 跑个大例子上个例子的语料库是非常小的文本,但实际情况是,语料库里会有百万上亿条文本,想想新华字典都那么厚。把语料全部存在RAM 不实际。假设文本放在一个文件夹里,一行话一行话的形式存储,gensim就可以实现一次返回一个句子的稀疏向量。 所以大例子的精华无非是,一次跑一条文本。点击这里下载样本’mycorpus.txt’ 输入: 123456789class MyCorpus(object): def __iter__(self): for line in open('mycorpus.txt'): # assume there's one document per line, tokens separated by whitespace yield dictionary.doc2bow(line.lower().split()) corpus_memory_friendly = MyCorpus() # doesn't load the corpus into memory!print(corpus_memory_friendly) # <__main__.MyCorpus object at 0x10d5690> 输入: 12for vector in corpus_memory_friendly: # load one vector into memory at a time print(vector) 输出: 123456789[(0, 1), (1, 1), (2, 1)][(2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)][(1, 1), (4, 1), (5, 1), (8, 1)][(0, 1), (5, 2), (8, 1)][(4, 1), (6, 1), (7, 1)][(9, 1)][(9, 1), (10, 1)][(9, 1), (10, 1), (11, 1)][(3, 1), (10, 1), (11, 1)] 虽然看起来结果跟跑个小例子一样,但是这个跑的过程对内存更友好。现在你可以随意扩充语料库。 接下来,生成字典,但无需一次性加载所有的文本到内存里,输入: 1234567891011>>> from six import iteritems>>> # collect statistics about all tokens>>> dictionary = corpora.Dictionary(line.lower().split() for line in open('mycorpus.txt'))>>> # remove stop words and words that appear only once>>> stop_ids = [dictionary.token2id[stopword] for stopword in stoplist>>> if stopword in dictionary.token2id]>>> once_ids = [tokenid for tokenid, docfreq in iteritems(dictionary.dfs) if docfreq == 1]>>> dictionary.filter_tokens(stop_ids + once_ids) # remove stop words and words that appear only once>>> dictionary.compactify() # remove gaps in id sequence after words that were removed>>> print(dictionary)Dictionary(12 unique tokens) Transformation现在已经向量化了语料,接下来可以使用各种向量转换transformation了,指的是把文档转化成另一个。在gensim中,文档用向量来表示,所以模型可以认为是在两个向量空间进行转换。这个转换是从语料训练集中学习出来的。 比较简单的一个叫TF-IDF。TF-IDF把词袋表达的向量转换到另一个向量空间,这个向量空间中,词频是根据语料中每个词的相对稀有程度(relative rarity)进行加权处理的。 看一个简单的例子。首先初始化一个tf-idf,在我们的语料中进行训练,然后对“system minors”进行处理。(参考) 输入: 1234567from gensim import modelstfidf = models.TfidfModel(bow_corpus)string = "system minors"string_bow = dictionary.doc2bow(string.lower().split())string_tfidf = tfidf[string_bow]print(string_bow)print(string_tfidf) 输出: 12[(5, 1), (11, 1)][(5, 0.5898341626740045), (11, 0.8075244024440723)] TF-IDF返回了一组元组。元组中第一个元素表示id,第二个表示tf-idf权重。注意到,“system”在原语料中出现4次,“minors”出现2次,所以第一个权重比第二个小。 其它的还有下面几个转换,具体转换代码点这里: Latent Semantic Indexing, LSI (or sometimes LSA) Random Projections, RP Latent Dirichlet Allocation, LDA Hierarchical Dirichlet Process, HDP 写在最后Word Embedding相关的有很多技术,pensim里也有更多好用的功能,比如word2vec,doc2vec等,这里只是抛砖引玉,举个小例子。跑一遍后,对这个词嵌入技术有个大概的感受就算目的达成了~😎]]></content>
<tags>
<tag>NLP</tag>
<tag>embedding</tag>
<tag>gensim</tag>
<tag>tf-idf</tag>
<tag>corpora</tag>
</tags>
</entry>
<entry>
<title><![CDATA[NLP笔记 - Getting Started]]></title>
<url>%2F2018%2F08%2F24%2FNLP%E7%AC%94%E8%AE%B0-Getting-Started%2F</url>
<content type="text"><![CDATA[背景: Getting started,入门指南。 NLP,natural language processing,无非是对文本数据做处理,可应用于智能对话(聊天机器人,例如 Siri/小冰),智能问答(智能客服),机器翻译,搜索引擎(google),等等。本篇主要介绍入门资料去哪里找,以及学习内容的优先级排序。 面向读者: 对nlp方向感兴趣,以做项目为导向的学习者 nlp零基础,希望快速入门 python选手 概念解释回顾一下人类是如何理解一段文字的,中英文的处理方式不同,以英文为例。一段话会被拆成一个个句子,一个句子又会被拆成一个个单词,根据单词在句子中的不同位置、单词的单复数、单词的时态等来理解。所以对文字进行分析的操作就很简单明了了。(参考链接) sentence segmentation(断句) 一般根据标点符号即可进行断句操作。以上面的动图为例,可以分成四个句子。 word tokenization(分词) 你可以很快知道“我爱钞票。”里“我”是一个词,“爱”是另外一个,“钞票”是另外另外一个词。但是机器不知道,所以要做分词。相较于中文,英文比较容易辨识词的属性。英文的句子由一个个单词组成,单词之间以空格隔开,因此空格之间就是一个单词。 “London is the capital and most populous city of England and the United Kingdom.” 上面这句话的分词结果如下,包含标点符号: “London”, “is”, “ the”, “capital”, “and”, “most”, “populous”, “city”, “of”, “England”, “and”, “the”, “United”, “Kingdom”, “.” parts-of-speech(词性标注) 区分一个单词是动词/名词/形容词/副词等。(想起曾经被语法支配的恐惧😭)这个词性标注的工作可以根据一个词性分类模型得出。 得出这句话中有名词、动词、限定词、连词、副词、形容词等。 text lemmatization(文本词性还原) 虽说英语是最简单的语义,但是不同词性的单词的变行还是很多的,比如单复数、be动词变形、动词是现在进行时还是过去时等,都还原成最初的样子。 identifying stop-words(识别停用词): 像 “and”, “the”, “a”, “of”, “for” 这种哪里都高频出现会造成统计噪音的词,被称为stop words。下面灰色的“the”, “and”, “most”均为停用词,一般会被直接过滤掉。正如维基所说,现在虽然停用词列表很多,但一定要根据实际情况进行配置。比如英语的the,通常情况是停用词,但很多乐队名字里有the这个词,The Doors, The Who,甚至有个乐队直接就叫The The!这个时候就不能看做是停用词了。 dependency-parsing(解析依赖关系) 解析句子中每个词之间的依赖关系,最终建立起一个关系依赖树。这个数的root是关键动词,从这个关键动词开始,把整个句子中的词都联系起来。 从这个关系树来看,主语是London,它和capital被be联系起来。然后计算机就知道,London is a capital。如此类推,我们的计算机就被训练的掌握越来越多的信息。 可以点击这个🔗链接自己尝试这个功能 named entity recognition(命名实体识别) 来给名词打标签。比如我们可以把第一句话当中的地理名称识别出来。 可以通过这个的链接,在线体验一下。随便复制粘贴一段英文,他会自动识别出里面包含哪些类别的名词。 conference resolution(共指消解) 指代词,比如他,它,这个,那个,前者,后者等。再比如缩写简称,北京大学通常称为北大,中华人民共和国通常就叫中国。这种现象,被称为共指现象。 word embedding(词嵌入):通常是深度学习第一步,将文本转换成数字形式,这样才能丢进去训练。将一句话变成一个向量,每个单词与数字一一对应。 word2vec GloVe sentiment analysis(情感分析):判断一段文字的情绪。比如淘宝评价文字是喜欢还是不喜欢这个商品,影评文字是看好还是不看好这个电影。 semantic retrieval(语义召回):把意思相同的信息从语料库/知识库中统统找出来。 matching(匹配) semantic matching(语义匹配):判断两句话说的是不是一个意思。比如在知乎提问后,系统需要搜索出相关问题的答案来显示。 term matching:所谓的 Ctrl+F,只匹配是否有这个词。比如搜索词是taxi,那么就算有‘的士’的信息也搜不出来。 智能问答框架一览以百度的开源AnyQ为例,这是一个问答系统框架: Question Analysis:来了一个问题先进行文字预处理,纠正错别字/命名实体识别/词性标注/词嵌入等。 Retrieval:可用深度学习神经网络进行语义召回,把相关的信息都找出来。 Matching:相关信息不一定是正确答案,可用深度学习进行语义匹配,找出最匹配的答案。 优秀的公开课 Dan Jurafsky & Chris Manning: Natural Language Processing [入门视频系列] Stanford CS224d: Deep Learning for Natural Language Processing [斯坦福系列,必看] Stanford CS224n: Natural Language Processing with Deep Learning Stanford CS224n 在b站上的视频 Stanford CS224d 在b站上的视频 Coursera: Introduction to Natural Language Processing [出自 University of Michigan] Awesome 系列awesome-nlp(website)[包含优秀的nlp教程/库/技术/开源数据/模型等,必看!] 里面的每一个链接都值得好好翻看翻看。重点介绍下面的几个python库: spaCy (website, blog) [Python; emerging open-source library with fantastic usage examples, API documentation, and demo applications] 这个库的链接博客值得看看,可以在上面的demo application上写自己的句子感受下语言是如何处理的,也可以尝试其他的demo和example,网站还是做的很用心的。 Natural Language Toolkit (nltk) (website, book) [Python; practical intro to programming for NLP, mainly used for teaching] gensim - Python library to conduct unsupervised semantic modelling from plain text 👍 这个库用来做词嵌入word embedding,将文字转换为数字,生成字典。 jieba - 适用于中文的分词工具 优秀的博客和资源 Deep Learning for NLP resources Quora: How do I learn Natural Language Processing? Google Research blog Explosion AI blog 52nlp Twitter: #nlproc, list of NLPers (by Jason Baldrige) twitter也是机器学习/深度学习的友好天地,很多post配图配文都很有意思,尤其是吐槽文😜 Reddit: /r/LanguageTechnology Medium: Nlp 优秀的书籍个人比较偏向于先看课件,有细节问题再回到书里去找答案。 Speech and Language Processing (Daniel Jurafsky and James H. Martin) [classic NLP textbook that covers all the basics, 3rd edition coming soon] Foundations of Statistical Natural Language Processing (Chris Manning and Hinrich Schütze) [more advanced, statistical NLP methods] Introduction to Information Retrieval (Chris Manning, Prabhakar Raghavan and Hinrich Schütze) [excellent reference on ranking/search] Neural Network Methods in Natural Language Processing (Yoav Goldberg) [deep intro to NN approaches to NLP, primer here] 开源的数据集 A thorough list of publicly available NLP data sets[开源数据大全,做项目不用愁数据了~] Quora问题匹配数据集下载链接 深度学习相关模型语义匹配的神经网络模型集合 语义匹配的神经网络相关模型: DSSM Siamese Network RNN RNN变种:LSTM、Match-LSTM、Seq-to-Seq、Attention机制 GitHub Awesome-Chinese-NLP 中文自然语言处理相关资料 NLP-progress 各种语言的NLP项目 练手项目 kaggle - 电影评论的情感分析 kaggle - Quora问题匹配 基于 spaCy 的断句/分词/停用词识别等基本操作 写在最后NLP技术的应用范围很广泛,可以抓住其中一个点来深入。根据跑上面几个例子,观察训练数据来对这个处理过程有个大概的理解。由于接触智能问答项目的缘故,接下来的笔记方向也是跟智能问答强相关。]]></content>
<tags>
<tag>NLP</tag>
<tag>getting started</tag>
<tag>tutorial</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Auto Machine Learning笔记 - Pipelines 制作教程]]></title>
<url>%2F2018%2F08%2F07%2FMachine%20Learning%E7%AC%94%E8%AE%B0%20-%20Pipelines%20%E5%88%B6%E4%BD%9C%E6%95%99%E7%A8%8B%2F</url>
<content type="text"><![CDATA[面向读者: 机器学习背景对 AutoML 感兴趣,熟悉并喜欢 sklearn 发现自己在相似分析中做着重复的步骤 kaggle 进阶者 背景: 本文是以一个文本数据处理的例子来展示pipeline如何把小功能串在一起,实现流水线操作。 Once you’ve gotten your feet wet in basic sklearn modeling, you might find yourself doing the same few steps over and over again in the same analysis. To get to the next level, pipelines are your friend! 有些东西你不知道,以为它不存在;一旦你知道后,发现满世界都是它。pipeline就是这样的。 概念解释pipeline(管道) 顾名思义就是把标准的/固有的建模过程流水线化。 假如你有一套通用的数据清洗流程,就可以写成一个pipeline,这样就不用根据不同的数据一遍遍的重复写这个清洗流程了。 pipeline是一块块的小逻辑的集成函数,尤其当模型十分复杂时,便于回头检查模型逻辑。 pipeline是一个类,一般继承sklearn的 BaseEstimator,TransformerMixin。 拥有 fit/transform/predict 等功能和属性。 下载数据集✔数据集下载链接 点击图片右上角的 ‘Download All ’,并解压数据集。 构建本地文件结构: 123456|-pipelines //文件名 |- pipeline.py //新建python文件 |- data //刚才下载且解压的数据集 |- train.csv //训练集 |- test.csv //测试集 |- sample_submission.csv //比赛结果提交样本,本文中用不到 打开pipeline.py,输入: 123456789import numpy as np # linear algebraimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)df = pd.read_csv('data/train.csv')df.dropna(axis=0)df.set_index('id', inplace = True)df.head() 输出: id text author id26305 This process, however, afforded me no means of… EAP id17569 It never once occurred to me that the fumbling… HPL id11008 In his left hand was a gold snuff box, from wh… EAP id27763 How lovely is spring As we looked from Windsor… MWS id12958 Finding nothing else, not even gold, the Super… HPL 可以看到数据集是文本信息,3列,包含id,text文本,和作者。这个比赛的原意是给出一段文字,预测是出自哪个作家之手,模型用来学习作家的文风。 文本特征预处理以下为适用于所有文本的数据清洗操作: 将文本信息去标点符号,且全部用小写字母 计算文本长度 计算文本字数 计算 非停用词 字数 计算 非停用词单词的 平均长度 计算逗号数 先用传统的统计方式来进行数据清洗,输入: 1234567891011121314151617181920212223242526import refrom nltk.corpus import stopwordsstopWords = set(stopwords.words('english')) # 可能需要手动下载 stopwords#creating a function to encapsulate preprocessing, to mkae it easy to replicate on submission datadef processing(df): #lowering and removing punctuation df['processed'] = df['text'].apply(lambda x: re.sub(r'[^\w\s]','', x.lower())) #numerical feature engineering #total length of sentence df['length'] = df['processed'].apply(lambda x: len(x)) #get number of words df['words'] = df['processed'].apply(lambda x: len(x.split(' '))) df['words_not_stopword'] = df['processed'].apply(lambda x: len([t for t in x.split(' ') if t not in stopWords])) #get the average word length df['avg_word_length'] = df['processed'].apply(lambda x: np.mean([len(t) for t in x.split(' ') if t not in stopWords]) if len([len(t) for t in x.split(' ') if t not in stopWords]) > 0 else 0) #get the average word length df['commas'] = df['text'].apply(lambda x: x.count(',')) return(df)df = processing(df)df.head() 输出: 创建 Pipeline拆分训练集和测试集,输入: 12345678from sklearn.model_selection import train_test_splitfeatures= [c for c in df.columns.values if c not in ['id','text','author']]numeric_features= [c for c in df.columns.values if c not in ['id','text','author','processed']]target = 'author'X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size=0.33, random_state=42)X_train.head() 输出: id processed length words words_not_stopword avg_word_length commas id19417 this panorama is indeed glorious and … 91 18 6 6.666667 1 id09522 there was a simple natural earnestness … 240 44 18 6.277778 4 id22732 who are you pray that i duc de lomelette … 387 74 38 5.552632 9 id10351 he had gone in the carriage to the nearest … 118 24 11 5.363636 0 id24580 there is no method in their proceedings … 71 13 5 7.000000 1 接下来是关键步骤。 根据特征是否为数值型,创建 两个selector transformers: TextSelector,NumberSelector selector的作用:输入一个column,根据这个selector transformer,输出得到一个新column 简单说就是,做 data transformation,收集想要的信息,比如 text length 输入: 1234567891011121314151617181920212223242526272829from sklearn.base import BaseEstimator, TransformerMixinclass TextSelector(BaseEstimator, TransformerMixin): """ Transformer to select a single column from the data frame to perform additional transformations on Use on text columns in the data """ def __init__(self, key): self.key = key def fit(self, X, y=None): return self def transform(self, X): return X[self.key] class NumberSelector(BaseEstimator, TransformerMixin): """ Transformer to select a single column from the data frame to perform additional transformations on Use on numeric columns in the data """ def __init__(self, key): self.key = key def fit(self, X, y=None): return self def transform(self, X): return X[[self.key]] 先来试一下 TextSelector 好不好用。由小变大,先创建一个mini pipeline,作用是先从数据集中抓取一列数据,再做tf-idf处理并返回结果。 创建过程只需传递一个格式如(名称,对象)的元组。括号左边是动作的名称,右边就是选取的列名。所以这个mini pipeline就是两个动作,selecting(选择一列)和tfidf-ing(对这列进行tf-idf处理)。 执行pipeline的命令,可以调用 text.fit() 来适应训练集,text.transform() 来应用于训练集,或者text.fit_transform() 来执行两者。 由于它是一个文本,它将返回一个稀疏矩阵,输入: 123456789from sklearn.pipeline import Pipelinefrom sklearn.feature_extraction.text import TfidfVectorizertext = Pipeline([ ('selector', TextSelector(key='processed')), ('tfidf', TfidfVectorizer( stop_words='english')) ])text.fit_transform(X_train) 输出: 接下来试一下 NumberSelector 对于数值型的特征处理好不好用,同样也先建立一个mini pipeline来观察效果。 这个pipeline操作就定为简单的scaler,一列列的进行数值的StandardScaler。先以 length列为例,仍然是两个步骤,先选列,即length列,再做数值StandardScaler。(StandardScaler是数据预处理的一个常见的数值缩放操作。) 输入: 12345678from sklearn.preprocessing import StandardScalerlength = Pipeline([ ('selector', NumberSelector(key='length')), ('standard', StandardScaler()) ])length.fit_transform(X_train) 输出: 根据输出结果可以看出,pipeline返回一个我们想要的数值缩放矩阵。然后把剩下的数值特征列都进行缩放scaler操作。当然这个数据处理操作你可以随意更改成其他可用的。 输入: 12345678910111213141516words = Pipeline([ ('selector', NumberSelector(key='words')), ('standard', StandardScaler()) ])words_not_stopword = Pipeline([ ('selector', NumberSelector(key='words_not_stopword')), ('standard', StandardScaler()) ])avg_word_length = Pipeline([ ('selector', NumberSelector(key='avg_word_length')), ('standard', StandardScaler()) ])commas = Pipeline([ ('selector', NumberSelector(key='commas')), ('standard', StandardScaler()), ]) 创建 FeatureUnionpipeline管道可大可小,又大又长又粗的pipeline也是由一个个mini pipelines组成的嘛。 接下来使用FeatureUnion来连接上面做好的pipelines,形成一个类似大的pipeline。 语法操作还是格式如(名称,对象)的元组。FeatureUnion本身不是pipeline,它只是一个组合,所以需要多写一行代码,将其变为一个大pipeline。然后的事情,你懂的,还是fit,transform,或者fit_transform操作。 输入: 1234567891011from sklearn.pipeline import FeatureUnionfeats = FeatureUnion([('text', text), ('length', length), ('words', words), ('words_not_stopword', words_not_stopword), ('avg_word_length', avg_word_length), ('commas', commas)])feature_processing = Pipeline([('feats', feats)])feature_processing.fit_transform(X_train) 输出: 甚至可以在刚刚的大pipeline尾巴上再添加一个分类器,即不仅仅是数据转化,而是增加建模/预测功能。还是原来的套路,写元组,再pipeline一下。 可以得到粗糙的 63.8%的分类精度。小试牛刀,不要太在意这些细节~ 输入: 1234567891011from sklearn.ensemble import RandomForestClassifierpipeline = Pipeline([ ('features',feats), ('classifier', RandomForestClassifier(random_state = 42)),])pipeline.fit(X_train, y_train)preds = pipeline.predict(X_test)np.mean(preds == y_test) 输出: 10.638347260909935 再看 Pipeline现在可以得出的结论就是,pipeline不仅能做数据预处理的流水线,更是能把整个建模套路做成流水线,只需在pipeline的结尾加上一个分类器。接下来将创建一个pipeline,完成上面所有的处理,最后用随机森林分类器。 优化 Pipeline利用 Cross Validation 寻找更优的pipeline,就要先观察pipeline的属性,再进行超参数调参。 输入: 1pipeline.get_params().keys() 输出: 这些都是pipeline相关的属性,即超参数,这些超参数的组合变化,超参数的数值变化都会影响一个pipeline好不好用。在此只为展示操作,因此随心情挑选四个超参数进行调优。优化方式为GridSearchCV,即 网格搜索交叉验证法,适用于少量的超参数个数和少量的数值候选调优。 输入: 1234567891011from sklearn.model_selection import GridSearchCVhyperparameters = { 'features__text__tfidf__max_df': [0.9, 0.95], 'features__text__tfidf__ngram_range': [(1,1), (1,2)], 'classifier__max_depth': [50, 70], 'classifier__min_samples_leaf': [1,2] }clf = GridSearchCV(pipeline, hyperparameters, cv=5) # Fit and tune modelclf.fit(X_train, y_train) 输出: 观察调优结果,即超参数最终选择的数值为多少,输入: 1clf.best_params_ 输出: 隐藏菜单操作为调用 refit,可自动使用使用pipeline来fit所有的训练数据。并将其应用于测试集。 输入: 1234567#refitting on entire training data using best settingsclf.refitpreds = clf.predict(X_test)probs = clf.predict_proba(X_test)np.mean(preds == y_test) 输出: 10.6425255338904364 还是有一点精度的提高的。 进行预测做模型总要有结果的,最后对数据集进行predict,看看未知文本到底是哪位作者写出来的概率更大。 输入: 123456789101112submission = pd.read_csv('data/test.csv')#preprocessingsubmission = processing(submission)predictions = clf.predict_proba(submission)preds = pd.DataFrame(data=predictions, columns = clf.best_estimator_.named_steps['classifier'].classes_)#generating a submission fileresult = pd.concat([submission[['id']], preds], axis=1)result.set_index('id', inplace = True)result.head() 输出: Pipeline 总结 sklean提供的pipeline来将多个学习器组成流水线,通常流水线的形式为: 将数据标准化的学习器—-特征提取的学习器—-执行预测的学习器/分类器 除了最后一个学习器之外,前面的所有学习器必须提供transform方法,该方法用于数据转 (例如: 归一化,正则化,以及特征提取) 参考链接 A Deep Dive Into Sklearn Pipelines Work like a Pro with Pipelines and Feature Unions Pipelines + GridSearch = Awesome ML pipelines Using scikit-learn Pipelines and FeatureUnions 优秀的Transformers与Pipeline]]></content>
<tags>
<tag>pipeline</tag>
<tag>sklearn</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Auto Machine Learning笔记 - Bayesian Optimization]]></title>
<url>%2F2018%2F07%2F31%2FAuto%20Hyperparameter%20Tuning%20-%20Bayesian%20Optimization%2F</url>
<content type="text"><![CDATA[优化器是机器学习中很重要的一个环节。当确定损失函数时,你需要一个优化器使损失函数的参数能够快速有效求解成功。优化器很大程度影响计算效率。越来越多的超参数调整是通过自动化方式完成,使用明智的搜索在更短的时间内找到最佳超参组合,无需在初始设置之外进行手动操作。贝叶斯优化(Bayesian Optimization)是基于模型的超参数优化,已应用于机器学习超参数调整,结果表明该方法可以在测试集上实现更好的性能,同时比随机搜索需要更少的迭代。此外,现在有许多Python库可以为任何机器学习模型简化实现贝叶斯超参数调整。 1. 超参数是什么? 在模型开始学习过程之前人为设置值的参数,而不是(像bias、weights)通过训练可得到的参数数据。 这些参数定义关于模型更高层次的概念(模型复杂性、学习能力等)。 比如说随机梯度下降算法中的学习速率/learning rate,出于计算复杂度和算法效率等,我们并不能从数据中直接学习一个比较不错的学习速度。但学习速率却又是十分重要的,较大的学习速率不易令模型收敛到较合适的较小值解,而较小的学习速率却又常常令模型的训练速度大大降低。对于像学习速率这样的超参数,我们通常需要在训练模型之前设定。因此,对于超参数众多的复杂模型,调超参技能显得很重要。 2. 常用的调超参方法有哪些? Grid Search 网格搜索/穷举搜索 搜索整个超参数空间,在高维空间容易遇到维度灾难,不实用。 网格搜索是一种昂贵的方法。假设我们有n个超参数,每个超参数有两个值,那么配置总数就是2的N次方。因此,仅在少量配置上进行网格搜索是可行的。 网格搜索可以并行化,使得网格搜索在足够的计算能力下更加可行。 每次trial之间是相互独立的,不能利用先验知识选择下一组超参数。 Random Search 随机搜索 稀疏的简单抽样,试验之间是相互独立的,不能利用先验知识选择下一组超参数。 超参通过并行选择,但试验次数要少得多,而性能却相当。一些超参可能会产生良好的性能,另一些不会。 Heuristic Tuning 手动调参 经验法,耗时长。 Automatic Hyperparameter Tuning 自动超参数调优 自动超参数调整形成了关于超参数设置和模型性能之间关系的知识,能利用先验知识选择下一组超参数。 首先在多个配置中收集性能,然后进行一些推断并确定接下来要尝试的配置。目的是在找到最佳状态时尽量减少试验次数。 这个过程本质上是顺序的,不容易并行化。 调整超参数的大多数方法都属于基于顺序模型的全局优化(SMBO)。这些方法使用代理函数来逼近真正的黑盒函数。SMBO的内部循环是对该替代品的优化,或者对代理进行某种转换。最大化此代理的配置将是下一个应该尝试的配置。SMBO算法在优化替代品的标准以及他们根据观察历史对替代品进行建模的方式上有所不同。最近在文献中提出了几种用于超参数的SMBO方法: Bayesian Optimization使用高斯过程对代理进行建模,通常优化 Expected Improvement(EI),这是新试验将在当前最佳观察上改进的预期概率。高斯过程是函数的分布。来自高斯过程的样本是整个函数。训练高斯过程涉及将此分布拟合到给定数据,以便生成接近观察数据的函数。使用高斯过程,可以计算搜索空间中任何点的EI。接下来将尝试给出最高的EI。贝叶斯优化通常为连续超参数(例如learning rate, regularization coefficient…)提供 non-trivial/off-the-grid 值,并且在一些好的数据集上击败人类表现。Spearmint是一个众所周知的贝叶斯优化实现。 SMAC使用随机森林对目标函数进行建模,从随机森林认为最优的区域(高EI)中抽取下一个点。 TPE是SMAC的改进版本,其中两个分离的模型用于模拟后验。众所周知的TPE实现是hyperopt。 3. 概念解释 高斯过程上图里那么多线就是高斯过程的体现。要使用贝叶斯优化,需要一种能灵活地在目标函数上建立分布的方法。这比在实数上建立分布更棘手,因为我们需要一个这样的分布来表示我们对每个x的f(x)的信念。如果x包含连续的超参数,那么我们必须为f(x)建模无限多的x,即构造它的分布。对于这个问题,高斯过程是一种优雅的方法。实际上,高斯过程生成多维高斯分布,并且存在足够灵活以模拟任何目标函数的模样。 Prior Function 先验函数基于概率分布,用于描述目标函数的分布,拟合目标函数曲线。不同的分布,PF不同,效果是不一样的。 Acquisition Function 收获函数 = max(mean + var)用于从候选集中选择一个新的点。贝叶斯优化的效果与AF的设计有较大的关系,由于此类function可能陷入局部最优解,因此在选点时,需考虑不能过早进入局部最优。AF计算EI,用来选择下一个采样点。 mean均值大:多去采样这些点会帮助我们更好的了解这个函数形态。 var方差大:表示我们对该点的了解甚少。 采样点每一个采样点就是原理解析里的黑点。每个采样点是基于前面n个点的多变量高斯分布的假设以及最大化AF而得到的,现目前为止认为的y的最大值最可能出现的位置。 一开始,采样数据少,算法会采标准差大的点。采样一定数目后,标准差的值会下降很多,此时采样点的选择就更多的受到均值的影响,采样点就更大概率的出现在真正最大值附近。 4. Bayesian Optimizer 原理解析贝叶斯优化基于高斯过程。 上图2个evaluations黑点,是两次评估后显示替代模型的初始值估计,会影响下一个点的选择,穿过这两个点的曲线可以画出非常多条,如上上图 红色虚线曲线是实际真正的目标函数 黑色实线曲线是代理模型的目标函数的均值 灰色区域是代理模型的目标函数的方差 只有两个点,拟合的效果稍差,根据下方的紫色的EI曲线,最左侧的最大值EI为下一个点 3个evaluations黑点 灰色区域是代理模型的目标函数的方差,黑点越多,灰色区域面积越小,误差越小 根据下方的紫色的EI曲线,左侧的最大值EI为第四个拟合点 4个evaluations黑点 黑点越多,灰色区域面积越小,误差越小,代理模型越接近真实模型的目标函数 根据下方的紫色的EI曲线,最大值EI为第五个拟合点,同理类推… 8个黑点 黑色代理曲线已经十分接近红色真实目标函数,灰色区域也越来越小,拟合效果不错。 5. Bayesian Optimizer 基本思想一句话总结:建立目标函数的概率模型,并用它来选择最有希望的超参数来评估真实的目标函数。基本思想是:利用先验知识逼近未知目标函数的后验分布从而调节超参。花一点时间选择下一个超参数,以减少对目标函数的调用。 建立代理模型的目标函数(Prior Function/先验函数) 找到在代理上表现最佳的超参数(利用EI值,根据Acquisition Function得出EI) 将这些超参数应用于真正的目标函数 更新包含新结果的代理模型 重复步骤2-4,直到达到最大迭代次数或时间 基于顺序模型的优化方法(SMBO)是贝叶斯优化的形式化。顺序是指一个接一个地运行试验,每次通过应用贝叶斯推理和更新概率模型(代理)来尝试更好的超参数。 6. Bayesian Optimizer 在python中的包Python中有几个贝叶斯优化库,它们在目标函数的代理算法上有所不同。 Spearmint(高斯过程代理) SMAC(随机森林回归) Hyperopt(Tree Parzen Estimator-TPE) 7. Bayesian Optimizer 优点 能利用先验知识高效地调节超参数,每个试验不独立,前一个推动下一个选择 通过减少计算任务而加速寻找最优参数的进程 不依赖人为猜测所需的样本量为多少,优化技术基于随机性,概率分布 在目标函数未知且计算复杂度高的情况下极其强大 通常适用于连续值的超参,例如 learning rate, regularization coefficient 在测试集表现优秀于手工调参结果,泛化性/鲁棒性好 不易陷入局部最优: EI的计算根据 Acquisition Function收获函数计算所得: 探索和开发是解释现象和优化算法的常用术语。 对于贝叶斯优化,一旦找到局部最优解,会在这个区域不断采样,很容易陷入局部最优。为了减轻这个,贝叶斯优化会使用“ 收获函数Acquisition Function ” 来平衡“探索”和“开发”,下一个点的选择要在这两者之间存在权衡。 下一个选择点(x)应该具有高均值(开发)和高方差(探索)。 8. 其他优秀文章与论文链接🔗 贝叶斯优化: 一种更好的超参数调优方式 哈佛教材:A Tutorial on Bayesian Optimization for Machine Learning Shallow Understanding on Bayesian Optimization 谷歌cloudml也在用贝叶斯优化 A Tutorial on Bayesian Optimization of Expensive Cost Functions, with Application to Active User Modeling and Hierarchical Reinforcement Learning Practical Bayesian Optimization of Machine Learning Algorithms Automated Machine Learning Hyperparameter Tuning in Python A Conceptual Explanation of Bayesian Hyperparameter Optimization for Machine Learning Introduction to Bayesian Optimization 写的不一定全对,如果有说错的地方,欢迎指正。]]></content>
<tags>
<tag>machine learning</tag>
<tag>Bayesian Optimizer</tag>
<tag>hyperparameter tuning</tag>
<tag>automl</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Auto Machine Learning笔记 - Auto-Sklearn]]></title>
<url>%2F2018%2F07%2F26%2FAutoML%2F</url>
<content type="text"><![CDATA[auto-sklearn: 基于sklearn/ AutoML 方向/ 免费自动机器学习服务/ GitHub开源/ 2.4k+ stars!!!⭐适读人群:有机器学习算法基础 学完 Machine Learning,又要学 Auto Machine Learning🙄 1. auto-sklearn 能 auto 到什么地步?在机器学习中的分类模型中: 常规 ML framework 如下图灰色部分:导入数据-数据清洗-特征工程-分类器-输出预测值 auto部分如下图绿色方框:在ML framework 左边新增 meta-learning,在右边新增 build-ensemble,对于调超参数,用的是贝叶斯优化。 自动学习样本数据: meta-learning,去学习样本数据的模样,自动推荐合适的模型。比如文本数据用什么模型比较好,比如很多的离散数据用什么模型好。 自动调超参:Bayesian optimizer,贝叶斯优化。 自动模型集成: build-ensemble,模型集成,在一般的比赛中都会用到的技巧。多个模型组合成一个更强更大的模型。往往能提高预测准确性。 CASH problem: AutoML as a Combined Algorithm Selection and Hyperparameter optimization (CASH) problem 也就是说,一般的分类或者回归的机器学习模型即将或者已经实现了低门槛或者零门槛甚至免费建模的程度。其实机器学习的每个步骤都可以向着自动化方向发展,而且自动化的方式又有很多种。机器学习自动化的难点还是在数据清洗和特征工程这些技巧,至于模型筛选、模型集成和超参数调参已经有比较成熟可用的代码了。我们的愿景是 人人都可以用得起机器学习系统🙂 有没有很google! 2. 目前有哪些公司在做AutoML,github上又有哪些开源项目?业界在 automl 上的进展: Google: Cloud AutoML, Google’s Prediction API Microsoft: Custom Vision, Azure Machine Learning Amazon: Amazon Machine Learning others: BigML.com, Wise.io, SkyTree.com, RapidMiner.com, Dato.com, Prediction.io, DataRobot.com github上的开源项目: auto-sklearn (2.4k stars!),论文链接:Efficient and Robust Automated Machine Learning ClimbsRocks/auto_ml,可以读一下代码学习如何写pipeline autokeras,基于keras的 automl 向开源项目 3. auto-sklearn的整体框架了解一下?呃…先凑活看吧,具体的可以到github上翻看文件结构。框架的主轴在第二列,第二列的精华在pipeline,pipeline的重点在components:(截至目前已经超出论文写的内容了,论文链接,相信会越来越丰富) 16 classifiers(可以被指定或者筛选,include_estimators=[“random_forest”, ]) adaboost, bernoulli_nb, decision_tree, extra_trees, gaussian_nb, gradient_boosting, k_nearest_neighbors, lda, liblinear_svc, libsvm_svc, multinomial_nb, passive_aggressive, qda, random_forest, sgd, xgradient_boosting 13 regressors(可以被指定或者筛选,exclude_estimators=None) adaboost, ard_regression, decision_tree, extra_trees, gaussian_process, gradient_boosting, k_nearest_neighbors, liblinear_svr, libsvm_svr, random_forest, ridge_regression, sgd, xgradient_boosting 18 feature preprocessing methods(这些过程可以被手动关闭全部或者部分,include_preprocessors=[“no_preprocessing”, ]) densifier, extra_trees_preproc_for_classification, extra_trees_preproc_for_regression, fast_ica,feature_agglomeration, kernel_pca, kitchen_sinks, liblinear_svc_preprocessor, no_preprocessing, nystroem_sampler, pca, polynomial, random_trees_embedding, select_percentile, select_percentile_classification, select_percentile_regression, select_rates, truncatedSVD 5 data preprocessing methods(这些过程不能被手动关闭) balancing, imputation, one_hot_encoding, rescaling, variance_threshold(看到这里已经有点惊喜了!点进去有不少内容) more than 110 hyperparameters其中参数include_estimators,要搜索的方法,exclude_estimators:为不搜索的方法.与参数include_estimators不兼容而include_preprocessors,可以参考手册中的内容 auto-sklearn是基于sklearn库,因此会有惊艳强大的模型库和数据/特征预处理库,专业出身的设定。 4. meta-learning 是什么操作?🔗poster What is MI-SMBO?Meta-learning Initialized Sequential Model-Based Bayesian Optimization What is meta-learning?Mimics human domain experts: use configurations which are known to work well on similar datasets 仿照人能积累经验的做法,使机器有[配置空间]去记录它们的经验值,有点像迁移学习 适用的程度,根据数据的相似度 meta-learning: warmstart the Bayesian optimization procedure 也就是学习算法工程师的建模习惯,比如看到什么类型的数据就会明白套用什么模型比较适合,去生产对于数据的 metafeatures: 左边:黑色的部分是标准贝叶斯优化流程,红色的是添加meta-learning的贝叶斯优化 右边:有 Metafeatures for the Iris dataset,描述数据长什么样的features,下面的公式是计算数据集与数据集的相似度的,只要发现相似的数据集,就可以根据经验来推荐好用的分类器。再来张大图感受下metafeatures到底长啥样:🔗论文链接:Initializing Bayesian Hyperparameter Optimization via Meta-Learning🔗supplementary.pdf 5. auto-sklearn 如何实现 自动超参数调参?概念解释 SMBO: Sequential Model-based Bayesian/Global Optimization,调超参的大多数方法基于SMBO SMAC: Sequential Model-based Algorithm Configuration,机器学习记录经验值的配置空间 TPE: Tree-structured Parzen Estimator 超参数调参方法: Grid Search 网格搜索/穷举搜索在高维空间不实用。 Random Search 随机搜索很多超参是通过并行选择的,它们之间是相互独立的。一些超参会产生良好的性能,另一些不会。 Heuristic Tuning 手动调参经验法,耗时长。(不知道经验法的英文是否可以这样表示) Automatic Hyperparameter Tuning Bayesian Optimization 能利用先验知识高效地调节超参数 通过减少计算任务而加速寻找最优参数的进程 不依赖人为猜测所需的样本量为多少,优化技术基于随机性,概率分布 在目标函数未知且计算复杂度高的情况下极其强大 通常适用于连续值的超参,例如 learning rate, regularization coefficient SMAC TPE 在 auto-sklearn 里,一直出现的 bayesian optimizer 就是答案。是利用贝叶斯优化进行自动调参的。🔗具体的贝叶斯优化原理链接🔗论文链接 6. auto-sklearn 如何实现 自动模型集成?官方回答:automated ensemble construction: use all classifiers that were found by Bayesian optimization目前在库中有16个分类器,根据贝叶斯优化找出最佳分类器组合,比如是(0.4 random forest + 0.2 sgd + 0.4 xgboost)可以根据fit完的分类器打印结果看最终的模型是由什么分类器组成,以及它们的参数数值:12345import autoskleran.classificationautoml = autosklearn.classification.AutoSklearnClassifier()automl.fit(X_train, y_train)automl.show_models() 打印automl.show_models()就能打印出所谓的自动集成模型有哪些,权重分布,以及超参数数值。 7. 如何使用 auto-sklearn? 适用系统:Linux 👍installation 官方文档🔗 接口文档🔗 举个栗子🔗 再举个栗子🔗 使用套路如下: 1234567# 4行代码搞定import autosklearn.classificationautoml = autosklearn.classification.AutoSKlearnClassifier()automl.fit(X_train, y_train) predictions = automl.predict(X_test) # 打印出0,1结果predictions_prob = automl.predict_proba(X_test) # 打印出0-1之间的概率值 亲测 X_train, y_train 内不能含有非数值型数据,比如Male/Female字母就报错。 训练集有哪些特征,测试集就必须有哪些特征,可以理解为不做特征筛选,所以最初导入训练集的特征越粗糙越好。 1automl.cv_results_ 会打印出非常非常多的东西,耐心看,会找到类似下面的规律。 123456789101112131415161718automl.sprint_statistics()# 打印结果如下:# 'auto-sklearn results:# Dataset name: 46b7545efa67d8cd76f70e71eb67b72e # Metric: accuracy# Best validation score: 0.955932 # Number of target algorithm runs: 1278# Number of successful target algorithm runs: 1252# Number of crashed target algorithm runs: 23# Number of target algorithms that exceeded the time limit: 3 # Number of target algorithms that exceeded the memory limit: 0'automl._get_automl_class()# 打印结果# autosklearn.automl.AutoMLClassifier 其他可以尝试的操作: 12345automl.score(X,y)automl.get_models_with_weights()automl.get_configuration_space(X,y) 8. auto-sklearn 目前有什么缺点 不支持深度学习,但是貌似会有AutoNet出来,像谷歌的cloud AutoML那样 计算时长往往一个小时以上 在数据清洗这块还需要人为参与,目前对非数值型数据不友好 9. AutoML 的发展情况随着这两天谷歌发布它们的 Cloud AutoML 各种惊艳的功能,对于这块的关注度会越来越高的吧~machine learning的比赛已经不足为奇啦,现在已经有很多有关AutoML的比赛了:http://automl.chalearn.org]]></content>
<tags>
<tag>machine learning</tag>
<tag>auto ml</tag>
<tag>auto sklearn</tag>
<tag>hyperparameter</tag>
<tag>model ensemble</tag>
</tags>
</entry>
<entry>
<title><![CDATA[25岁学到的25件事情]]></title>
<url>%2F2018%2F07%2F21%2F25%E5%B2%81%E5%AD%A6%E5%88%B0%E7%9A%8425%E4%BB%B6%E4%BA%8B%E6%83%85%2F</url>
<content type="text"><![CDATA[这是我比较喜欢看的一类文章,也算是我的三观/ 价值观的年度总结。24岁的我,喜欢清零,喜欢去推翻过去的自己,喜欢挣扎,喜欢折腾。 Collecting what I’ve learned in my 24……100% |████████████████████████████████| 24G 1G/s Initializing…… 25岁初始值设置如下: 1. 没人能够定义你,除了你自己你可以是一个厨师、模特、摄影师、音乐家、工程师,或者斜杠青年;你可以是乐观的、偏执的、内心扭曲的、开朗的,或者多重人格的;定义一个人的维度非常多。这就意味着,在你有限的生命里, 你可以自定义自己的维度量级。今天的你可以是3维的,你是一个会拍照的/经常煮好吃早餐的/算法工程师。明天的你就可以是4维的,明天是一个会拍照的/经常煮好吃早餐的/会画画的/算法工程师。 你可以自定义维度的排列组合。上个月的你是会打架子鼓的前端工程师,这个月就可以去尝试成为一个会跳舞的美妆博主。 如果你觉得别人定义了你,比如你说父母为你准备了以后的道路,他们希望你成为一个公务员云云之类的。首先,请你记住,也请你承认! 是你自己允许,是你授予别人/授予你父母定义你的权力。 只要你不允许,只要你敢对抗你的奴性,你就能主宰你自己。 你做的事情,定义了你是个什么样的人。 我当然知道转型和变化是需要付出代价的,可就是因为有挑战性才好玩的嘛。普通玩家才玩标配,高级玩家都开启自定义模式。 2. 自信的人永远是美的每个人心中的美应该是不一样的,无论是肤色深浅/发型长短/眼睛大小/腰腿粗细,只要是不影响你的健康的身材,都是美的。不要陷入无限否定自己的循环中,简直是自我毁灭。多肯定自己。主流不一定是正确的选择,毕竟真理还掌握在少数人手中呢。那些真正爱你的人,会发现你的美,他们更关心你快不快乐,健不健康。至少我的偶像不一定都长的好看,但一定是超级自信的。自信的人就是牛逼哄哄的! 3. 年龄这个数字,什么都说明不了年龄代表不了一个人的健康状况,代表不了性格,更代表不了一个人的学识程度。有时候,不听老人言,能开心个好几年。如果你自己拿年龄说事,30岁想转行很犹豫?40岁不该重回大学?50岁不该寻找轰轰烈烈的爱情?有些门槛是人为定的,因为他们想保护自己的圈子。不要让别人告诉你什么该做什么不该做,让他们搞清楚他们自己的人生就不错了。别人不能拦着你做想做的事,你自己也别拦着自己。 什么时候结束一段糟糕的感情都不晚。什么时候开始一场美好的征程都不晚。什么都是从零开始的,你着急也没用,一旦开始,一旦去做了,就不那么焦虑了。 年龄是你自己强加给自己的假如30岁的你有一天失忆了,被告知成25岁,又有何违和感呢? 年龄这东西太虚了,just let it go ~同理,成绩好的不一定人品好,名气大不一定代表优秀。 4. 很多事情,没有唯一的标准 很多事情都没有唯一标准,甚至没有对错。你要动脑子,同时不要让常识(别人的说法)影响你的判断。比如上不上大学这件事,上大学好像是顺其自然的一个必选项,很多人会问18岁不去上大学去干嘛?但是,不上大学是错的吗?现在很多名人用他们自己的人生证明了不上大学不一定是个不正确的选择。 在没有对错之分,没有是非黑白的时候,自己的三观/价值观就是一把标杆尺子。你觉得这样是正确的,同时别人也可能觉得这样是错误的。 此时不要觉得这世界上的人黑白不分了,此时要用你的知识库形成你的观点,并且能用事实去证明你的观点。你可以质疑自己,反驳自己来加强自己的观点,经过考验后你有一个个坚定的想法,形成你心中的尺子,再去衡量眼前的这个世界。 如果之后真的遇到推翻你三观的事情,那么你需要重新思考了。你正在接触一个更大的世界。 5. 找工作,挺看缘分的找工作,它衡量的因素太多了,你不一定成绩好项目多就能去你想去的地方,总有阴差阳错的事情发生。当被拒绝的时候,别太当回事,因为这真的很正常。当然有些反思工作还是要做的,比如自己是否还缺哪部分的能力需要补齐~ 6. 很多事情都是纸老虎,你去做就知道了经常告诉自己,对于想做的事,有60%的自信就够了。不要等什么都要准备好,要与这个世界对话,告诉外界我想做这个,而且我还会做的很不错。当然也可以闷声闷响的去做,总之去做就对了!很多东西看起来好像很高大上,很多人看起来好像很牛逼,等你一步步逼近他们的时候你就不会那么觉得了。所以才会有很多厉害的人觉得自己是骗子这种假象,别人说你厉害的时候,别太当回事了。同时,要学会借力 不要重复造轮子。 7. 享受孤单不为了合群而合群,时间很宝贵,不要花在迎合别人上。把时间花在哪里,你就属于哪里。花时间去和自己碰撞,有所反弹才是对自己的认知的开始。曾经的我对外界的要求很低很低,因为我不知道自己是谁,自己的价值有多高,三k一个月的实习从来不觉得委屈。等到不断与厉害的人碰撞之后开始意识到自己升值的空间,才会在正式找工作时可以谈判自己的理想收入。太多时候,的确需要其他人的陪伴,需要外界的肯定,但是也越来越觉得,向内寻求肯定才是正事。寻找自己对自己的肯定,找到自己对自己的那一份支持,真的比其他任何人的分量都重!不仅要做自己,还要做一个能照亮自己的人。现在的自己有点反社交的状态,只要有空更喜欢一个人呆着,更喜欢去了解自己到底在想什么,喜欢什么,想做什么。 8. 不要急着say no 多说why not? 为什么不呢?前几年经常被挤出舒适圈子,被动接受改变有时候也不是坏事。跳出一个熟悉的圈子,会迫使我去靠近跟我喜好更接近的圈子,会让我更接近真正属于我自己的圈子。 熟人未必适合同路。 还有很多未知的事情从四面八方向你靠近,不要急着say no,可以试试强迫自己说yes。比如朋友问你周末要不要一起健身,先去体验一下也无妨。比如我就会强迫自己今天出门就去吃一家没吃过的餐厅。好玩的是,大多数情况我还蛮喜欢刚刚接触的东西。 有时也可以学会主动离开,主动离开舒适圈,主动去尝试新的体验。我现在天天过着不舒适的生活,每天都在推翻昨天的自己,尝试新东西,但是我知道我正在走上坡路。 9. 事情是一件件做完的每次面前堆着很多To-do list时,我都一遍遍跟自己这样强调,事情是一件件做完的。平时可以训练自己为难的大的事情慢慢做铺垫。 10. 看东西看本质,不要徒有虚表很反感网上天天卖焦虑的那一套,说你再不学编程,就要被机器人淘汰了!说的跟真的似的,难道编程是个全民的职业吗?这是一个分工合作的社会,怎么就变成了只需要敲代码的了? 多想想公众号、大V们为什么说这样的话为什么卖焦虑?因为他是卖课的,你会看见文章最后有个999元的python课程。这动机,很明显了吧。编程还好多种语言呢,你问问他学哪个好。写这篇文章的人也未必会写个hello world。 不需要日积月累的匠人精神的东西就是没什么可吹嘘的喝酒泡吧纹身、跳伞潜海冲浪,这些看似很酷其实零门槛,尝试一次就能装X的事其实也就那样。那种到70岁还在敲代码做项目的才酷,那种20年如一日给家人做饭的妈妈们才酷,那种坚持做一件事的人,才酷。要知道什么叫冷静,什么叫沉淀,什么叫不浮躁,什么叫坚持。 11. 你朋友圈再厉害,又怎么样呢再厉害都是别人的厉害,只要自己不去做,就算朋友圈都是大牛,又能沾多少光呢。而且大牛们又不是傻子。很多人喜欢抱大腿,让别人把你的事也做了。抱的了一时,不如自己成为大腿。掌握自己人生命脉的感觉真的很好。 12. 做你喜欢的事,你会发光找到自己想做的事,很容易。找到后坚持去做这件事,不容易。所以找一样终身热爱的事情,可以花一辈子去做的事情,很重要。Nobody says it was easy, but it’s important.人是要有主心骨的,知道在哪里找到自己,释放自己。 13. 经常记录自己的想法因为就是那么一下子,你感觉受到了神的指点,但是你以后却再也想不起来了。我自己有写日记的习惯,学习算法时经常做笔记,而且每个月、甚至每天都有to-do-list。列表上一定有截止日期,可以拖延,但一定会去做完。记录想法,是一个与自己对话的过程,是一个了解、成长的过程。 14. 犯困了就趴下睡觉不要强迫自己不睡,别揪头发,别拿笔戳大腿。如果能做到清醒你早就不瞌睡了。 15. 不要轻易相信人世界真的很大,远比你想的邪恶的多,远比你想的美好的多,我觉得好人更多。同时,世界上有很多坏人,不要忍气吞声。 16. 专业知识很重要有一技之长很重要,至少对我来说是这样的。如果再给我一个机会,我还是会爱柯西定理爱拉格朗日中值定理爱牛顿-莱布尼茨爱高斯过程爱数学,刷多少课后题我都愿意。脑子是个好东西,越用越有意思。有一个说法是脑子分两个区:快思考和慢思考。第一个是直接反映,像条件反射,比如问你1+1等于几。第二个是需要集中注意力,比如23×12等于几。推荐一本书,Think fast and slow.提升专业知识意味着,经常做一些需要第二脑子的思考。很反感用凭直觉来说事的。 17. 多思考死亡60年,转瞬即逝。等你60岁的时候你将会这样告诉自己。多思考死亡的好处是,你会反向看待你的人生,你从人之将死的那一刻回到活蹦乱跳的现在,你的内心会充满感激,你不会计较那么多。你会找到对你最重要的东西。一个人只能活一次,一定要明白这个意味着什么。 18. 清零,永远突破自己偶像:Musk / 雷军 19. 钱很重要,理财很重要女性有经济收入很重要,甚至说,女性一定要有自己的经济收入。这才是谈平等的条件。 20. 身上最性感的地方,是脑子😊21. 做长远计划 想明天到达,今天就得出发。如果你能明白这句话,就懂我想说什么了。 做长远计划,3-5年,甚至10年,再倒推看看现在应该做什么。很多事情你预想不到,但是你还是要想。 22. 害怕什么就直面什么逃避可耻但有用,曾经陪伴了我很长一段时间。后来我才发现,说这句话的人是没有直面事实的勇气。因为你躲过的东西还是会回头来找你的。这种情况在我做数学题目的时候尤其明显,不会做的题会一直出现,直到我真正弄懂。类比生活考题。 23. 帮助他人时,不要想回报如果你想帮别人,一定是要出于你完全自愿,不要想着人家来日涌泉相报。省省吧,很多情况根本不会有任何回音,甚至谢谢都没有。你要享受帮助别人的这个过程,这是你做这个动作的唯一回报。如此没有期待,你才不会失望。 24. 父母能陪伴你的时间是有限的每次想到这个真的会很无力。敲完这一句,就去定回家的机票。 25. 此生,无需他人庇佑]]></content>
<tags>
<tag>life lesson</tag>
<tag>birthday</tag>
</tags>
</entry>
<entry>
<title><![CDATA[我喜欢的个人网站]]></title>
<url>%2F2018%2F07%2F12%2F%E6%88%91%E5%96%9C%E6%AC%A2%E7%9A%84%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99-updating%2F</url>
<content type="text"><![CDATA[我搭建个人网站的初衷是因为: 工作选择了机器学习方向,日常理论学习,写代码,有一个记录的过程很重要 没有真正弄懂的问题会重复遇到,翻看自己原来的笔记的频率非常高 喜欢有设计感有创意有风格的事物,喜欢乱涂乱画乱写,喜欢从0到1 需要有一个深度思考的地方 之前有写文章介绍如何搭建自己的个人网站:🔗如何搭建自己的个人网站(上)🔗如何搭建自己的个人网站(下) 就在参考别人的学习笔记的过程中,发现好内容很多都来源于他们的个人网站或者CSDN。而且,个人网站的作用不单单是理论学习笔记,也可以有自己生活的记录,和其他内容的分享。当自己有意识的去观察别人的网站后,遇见优秀的个人网站像是挖到宝一样开心😁虽然很多内容还是基于机器学习的。入选的原因:可能是页面设计很抓人,可能是文字质量特别高,可能是生活三观有共鸣,总之都是值得学习的~不想偷偷私藏,拿出来跟大家一起分享~ 如果你是: 想成为一个拥有鲜明风格的个人网站博主 喜欢欣赏美好的事物 喜欢不断的探索自己 那么这篇文章就很适合你了❤ 正文:主要以【网站链接】-【简介】-【吸引我的地方】来介绍它们 个人风格比较强烈的 1. 🔗pluskid.org这是一个各种开外挂的MIT高材生博主:学术、旅行、绘画、摄影、写诗… 🔗freemind.pluskid.org这是他的技术博客,主要记录一些机器学习、程序设计内容。放出一篇被实力圈粉的文章:2017 风衣 下面是他在旅途中的travel sketches:下面是他的平时一些绘画和摄影作品: 2. 🔗www.kennethreitz.org这位兄弟因为他的减肥励志故事已经在技术圈内外都大名鼎鼎了,下面是写他的一些文章:有意思是他的菜单栏,表明了他的多重身份:程序员、摄影师、出专辑、拍视频、演讲、发表论文、旅行、还有vlog,真心很潮了而且每个模块的内容都相当的丰富! 3. 🔗www.ruanyifeng.com 阮一峰的个人网站第一次对阮一峰老师产生好奇心是有朋友说我是女版阮一峰😂,总感觉这个名字好像哪里见过。很快就发现早在阅读《黑客和画家》的时候就在封面见过阮一峰老师的名字,虽然我强迫自己读的是英文版。为什么特别尊敬阮一峰老师是因为,重点看下图的几个数字: 1710篇文章 平均两三天更新一篇文章 2本书:《未来世界的幸存者》和《前方的路》 16个文章分类:算法与数学、创业、科技、英语、开发者手册、散文…凭良心说,每篇文章的质量都在线,看底下的评论数就知道了。至于排版设计…em…人无完人😂 接下来是一些列机器学习、程序设计等强相关的技术博客:如果每个博客都进行图文介绍会造成灾难的,所以下面的技术博客就只放链接和简短介绍了,相信我的话就请放心的点开吧! 🔗dnc1994.com ,在 Kaggle 首战中进入前 10% 🔗godweiyang.com ,机器学习,数学课程笔记 🔗geekplux.com,优秀的前端,优秀的可视化项目 🔗redstonewill.github.io ,主要介绍吴恩达课程笔记,有公众号 🔗liuchi.coding.me ,优秀的面经写手 🔗d0evi1.com ,深度学习,写的很仔细 🔗yihui.name ,谢益辉,就职于 RStudio,about页面写的很有趣 🔗shuang0420.com ,徐阿衡,大量NLP学习笔记 🔗yongyuan.name ,个人网站有简历可参考,图像处理方向 🔗WILDML ,Denny Britz,Google Brain Team, 深度学习,NLP 🔗PHILIPP SINGER ,Senior Data Scientist,kaggle大神 🔗Andrej Karpathy ,Director of AI at Tesla,Stanford Computer Science Ph.D. student 🔗colah’s blog ,对机器学习的一些概念解释的很清楚,尤其LSTM那篇 🔗小土刀 ,刀神的网站 🔗Daniel Balle ,干净的网站,只剩简历和作品 🔗Hux Blog ,前端工程师,网站很好看,开发了hexo主题 🔗reuixiy ,感觉是挺会折腾的一个博主 🔗liaopeiyuan.github.io ,被封面吸引 🔗Nadieh Bremer ,惊艳的个人数据可视化分析师,有空一定要学习一下 写在最后 建立网站是最简单的事,谁都可以花两三个小时搭一个。难的是维护网站的过程。很多苦心经营的个人网站都是比较凄凉,无人问津,导致很多优秀的个人网站的停更。显然的,大众都喜欢碎片短文,比如微博,比如微信公众号。但我还是认为深度思考很重要,你还记不记得那个坐在图书馆一个月熬出一篇论文的你? 只有喜欢的事情才会坚持不断的去做,希望自己能在网站维护这件事上坚持下去 做过的事不会没有用,功不唐捐 还有,谁说搞技术的都是呆子,啪啪啪打脸 关注这些个人网站的好处有很多:可以看看同行或者你喜欢的人都在忙什么,下一步有什么打算,甚至可以互相坚持鼓励。我就很期待认识这些网页背后的人😊 会持续更新的…下一个上榜的会是谁呢? 写在最最后目前关于网站推荐的更新工作已经挪至“Friends”页面,本文不再更新,传送:友情链接]]></content>
<tags>
<tag>personal website</tag>
<tag>design</tag>
</tags>
</entry>
<entry>
<title><![CDATA[参加CCF-GAIR(2018)的体验总结]]></title>
<url>%2F2018%2F07%2F10%2F%E5%8F%82%E5%8A%A0CCF-GAIR-2018-%E7%9A%84%E4%BD%93%E9%AA%8C%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[warming-up video, 推荐观看~❤ CCF-GAIR,全球人工智能与机器人峰会,由中国计算机学会(CCF)主办,雷锋网、香港中文大学(深圳)承办。 一年一度的人工智能分享大会,2018年是第三届。 CCF-GAIR 2018 提供1个主会场和11个专场: 2018届参会的价值: 三天的行程,11个专场,基本涵盖目前人工智能的应用场景 每个专场的演讲内容很厚实,100多位演讲嘉宾都是实打实的厉害,图灵奖得主、中科院院士及藤校教授等具体的嘉宾可参阅:CCF-GAIR 2018 官网 接下来就按照我pick的专场来进行学习和总结: 正文 - 参加 CCF-GAIR 的笔记[AI前沿专场] ===========================走向真正的人工智能 (张钹,中国科学院院士,清华大学教授)中心思想:现在离真正的人工智能还有一段很长的路目前人工智能取得成功的因素: 大数据 计算能力提高 优秀的算法 必须建立在合适的应用场景下 目前的人工智能的实现有5个限制:(重点!!📍) having rich data or knowledge丰富的数据或者知识(关于这点不能同意更多!没数据就叫你建模的都是耍流氓!!) certain information 确定性信息 perfect information 完全信息 ‘static’ 静态的 single task and finite domain 单任务,有限领域(AlphaGo也只会下象棋而已嘛!人能做的太多太多了) 张钹: 大家常常关心什么样的工作会被机器所替代,我可以明确告诉大家,满足这 5 个条件的工作,总有一天会被计算机取代,就是那些照章办事,不需要任何灵活性的工作。 为什么有这 5 个限制?原因在于我们现在的人工智能是没有理解的人工智能。 智能体现在推理能力上。但是很不幸,现在的对话系统推理能力都很差。基本做法是建立一个常识图谱,用这个图谱帮助理解提出的「问题」,同时利用常识图谱帮助产生合适的答案。 这种通过数据驱动做出来的系统,它的性能跟人类差别非常大,鲁棒性很差,很容易受干扰,会发生重大的错误,需要大量的训练样本。 这样的系统只是一个机械的分类器,根本不是感知系统。 人类的最大的优点是「小错不断、大错不犯」,机器最大的缺点是「小错不犯,一犯就犯大错」 大家为什么这么重视人工智能?因为我们永远在路上,这就吸引我们去解决这些问题,这些问题一旦解决了,人类的社会进步、人类的生活就会发生本质上的改变。 AI赋能,人机共融 (张建伟,德国汉堡科学院院士,汉堡大学多模式系统研究所所长,国家千人计划专家)单模态学习应用 (推荐《机智过人》节目,寓教于乐) 图像处理中,对于模糊的图像,人类在模糊信息下运用知识、运用外推的能力强于机器人 看图写诗:微软机器人小冰 声音的单模态学习 机器人阅片,医疗运用,机器人在有限环境里的大数据学习能力非常强 跨模态学习应用 跨模态的动态适应机制例如,通过发现老鼠在学习前和学习后的神经元的变化,希望总结出未来更好的带有局部记忆的深度神经模型。 跨模态的泛化和预测 跨模态的人机交互方面如何让机器人通过视觉、语言的共同学习,更好地理解概念,理解他们中间的关系。 通过多模态的学习,包括未来的制药、科学实验,都可以通过机器人进行大量的加速,在机器人应用比较典型的瓶颈问题里,通过多模态的学习实现了机器人的灵巧操作,包括抓取、注射等等。 未来关键技术举例 区块链技术产生可靠学习数据 日常复杂环境的快速理解 柔性轻型机器手臂和灵巧手的设计 基于传感的精细灵巧操作 机器人高层知识的获取和学习 自然人机交互 跨平台运行的开源软件 … Driving Safety and Autonomy through Advanced Sensing Technologies and Efficient Big Data Analytics(Mircea Gradu,2018 汽车工程学会(SAE International)主席,Velodyne LiDAR 高级副总裁兼CQO) 这个video展示了Velodyne LiDAR在自动驾驶上技术实现的可视化情况 Rethinking Deep Learning: Back to the Challenges of Computer Vision(马毅,加州大学伯克利分校电子工程与计算机系教授, ACM 、IEEE Fellow)这是整场大会100多大牛里我最喜欢的嘉宾,不是因为他title最高或者是颜值最高,而是因为他敢于表达自己的观点,能跳出圈子思考先放出他的观点:Can we all please agree: With back-propagation and sufficient computation cost, deep models can fit or over-fit any finite (polynomial #) samples. So-learned models are useful when such finite samples cover almost all cases of interest or importance. But let us be honest that: Wishful designs and empirical validations are not mathematical proofs.(however big data are always negligibly small to infinity!) Learning is about finding the simplest generative model from finite samples.(without which there is no “stability, robustness, invariance, or generalizability”.) And also realize that: Deep networks are trying to find low-dimensional structures, too!(hence the theory of subspace, sparsity, low-dimensional models is foundational.) 下面说一下我的总结: 关于深度学习预先科普,按照顺序是前者包含后者:人工智能-机器学习-深度学习也就是,机器学习是人工智能的一个分支,深度学习是机器学习的一个分支。 作为一个数学出身的工程师,我非常理解并且比较赞同马毅老师对待深度学习的观点。就是不盲目的崇拜深度学习。现在的深度学习,各种神经网络的火爆,是没有完整的数学论证作为支撑的。而是放在现实场景中比较好用,比如人脸识别,就这么被大众以及学术圈子接受了。但是图像做细微肉眼不可见的改动,机器可能就识别不出人还是狗,人却一定不会犯这么低级的错误,深度学习它的不可解释性或者弱解释性还是它的痛点。 深度学习是“经验”的学习与应用,而传统的像压缩感知(compressed sensing)往往是严谨的数学建模,即所谓的model-based。 深度学习有些部分其实是根植于传统的模型与方法,像经常提的dropout扔参数,其实也是稀疏性的一种体现。 深度学习的有些东西就是传统的机器学习,只不过是换了一个名字而已。像经常提到的前馈、反馈,尤其是根据结果往前推的反馈不就是机器学习里面的监督学习吗?前馈一直往前走,相当于没有利用标签信息,不就是无监督学习吗? 深度学习网络层数越深越好的观点值得商榷,PCANET就是一个例证(没看过…)。深度学习深的应该是理论,是理解,而非单纯的加层数。马老师讲了一句爱因斯坦老人家的话,大概是”Everything should be made as simple as possible, but not simpler” 但是话又说回来,与其等待解释性更强的算法达到更好的性能和效果,现在的深度学习都是未来优秀算法的垫脚石,如果不能一步到达未来,那不就应该先利用现有的资源做现在能做的事吗? 关于考古、文献阅读我们现在一般只读最新的文献,马老师的一个观点也非常值得思考:你想到的、遇到的问题老祖宗可能早就想到、遇到了,甚至可能给出了还不错的解法!也就是说,我们读文献的时候不要仅仅局限于新文献,也去尝试一下老一点的甚至50、60年代的文献,虽然处理的问题很可能完全不同,但这并不代表着我们不能从旧文献中获得启发。不要重复造轮子,就是这个意思吗? 关于思考物理有质量守恒,数学有难度守恒。同时,作为一个技术人员,表达能力十分重要,与别人分享,首先要引起别人对你的项目的兴趣、共鸣,其次能够解释明白思路与框架,最后要敢于表达自己的观点。而且在开发算法或者开发模型的时候,不能一头扎进去沉迷算法结构的优美,而是时不时要出来审度,思考应用的场合,方向和远见有时候更重要。很喜欢马毅老师这种对万事保留理性的质疑的态度,做数学的就是要有这种强迫症才可爱吧😝 [仿生机器人专场] ===========================仿生粘附和软微型机器人(Metin Sitti ,德国马克斯普朗克智能系统研究所的物理智能部门元老级成员及负责人,nanoGriptech 公司创始人,原卡内基梅隆大学的机械工程学教授)他首先介绍了自己早期关于壁虎的黏着原理的仿生学研究。壁虎的指头上有纤维,提供了很强的吸附力,而不需要像昆虫一样需要黏液,是一种干性黏性。迷妹角度❤❤ 视频内有一个小虫子一样的不明物体对不对~~这是一种大约七分之一英寸长的机器人,它首先看起来只不过是一条橡皮状的小条状物。然后它开始移动。它可以走路,跳跃,爬行,翻滚和游泳。它甚至爬出游泳池,从水环境变成干燥的环境。具体操作可以点击下面的视频: 机器人原型足够小,可以在胃或泌尿系统中移动。 该机器人尚未在人体中进行过测试,但其目标是改善其用于医疗用途。 例如,将药物输送到体内的目标。比如服用药物后,药物能直接跑去你体内伤口或者需要药物的地方,而不是散落在肠胃的各处。 Sitti博士说,这项研究最不寻常的是,这种“极简主义机器人”可以实现“在复杂环境中导航的所有不同类型的运动可能性”。 接下来总结一下这个小机器人的存在意义,别看体积小,其实挺有趣也挺有用的: 机器人是由什么构成的,它是如何工作的? Sitti:机器人由弹性橡胶制成,里面充满了许多磁性小颗粒。编程可以操作这些粒子的磁性,以便从外部,当我们施加磁场时,弹性鞘形机器人将其形状改变为我们想要的任何东西。 然后它做所有这些不同的动作。当你看到这个小东西爬行和跳跃以及所有这些东西时,它看起来像一个生物。 这个机器人的未来在哪里? Sitti:目前的主要目标之一是将这个微小的软机器人放入我们的消化系统或泌尿系统 - 以及将来的血管系统 - 并使其能够在所有这些复杂的组织中导航,这些表面完全充满了流体或半填充,或无流体。 如果你看一下我们所拥有的医疗设备,最小的是直径为毫米的导管,它们总是被束缚住。因此,制造这个小型机器人的主要目标是真正进入我们体内难以触及甚至不可能到达的区域,并且侵入最小。 机器人已经足够小,可以用于我们的消化系统和泌尿系统。我们想要更小,甚至几十微米,这样我们几乎可以到达你体内的任何地方。 有朝一日这个小机器人能够提供药物? Sitti:我们一直在探索的功能之一是如何在体内输送可能是药物的货物。有不同的方式。通过改变形状,我们可以抓住货物,然后通过打开形状来交付货物。 第二种方式是我们在机器人上做一个小口袋,只打开一个我们可以控制的特殊形状变化。 灵感来自水母,毛毛虫和其他动物的运动? Sitti:基本上,我们采取了所有这些灵感,并将它们合并为一个机器人。这是我们在这项研究中解决的另一个科学挑战:如何将毛虫,水母和所有这些不同的,小的,柔软的生物结合到一个相对极简主义的机器人中,可以实现在复杂环境中导航的所有不同类型的运动。 如果它在体内迷失了怎么办? Sitti:这个版本作为一个整体机器人不是完全可生物降解的。我们正在研究的一个项目是制造一个完全可生物降解的机器人。最后,机器人将被身体溶解,没有副作用,没有毒性,也没有任何材料会导致身体出现任何问题。这是我们小组的主要目标之一。这是可能的。我的意思是,我们的弹性体在体内完全可降解。我们的磁性纳米粒子可在体内完全降解。这只是整合它们的问题。 智能仿生机器鱼 (谢广明 ,北京大学工学院教授)能实现后空翻/垂直旋转的鱼:实现机器鱼群:详细链接🔗 机器人比较好理解,有没有跟我一样好奇机器鱼是干嘛用的?鱼跟海洋有密不可分的关系,目前的仿生鱼能做到的有: 自动带回指定海洋域的水样本,供水质检测用,就不需要人划船过去采水样了 能带领生物鱼群游动,在捕鱼期间,可以放出所需鱼群样子的鱼把生物鱼群带领到捕猎区域,可以有效筛选掉不需要的鱼种。当然基于合理捕猎的范围。 装有摄像头的鱼可以日常监控海洋可见范围内的情况 做海洋的清洁工,把人类丢进去的大量垃圾回收,尤其是将塑料袋/塑料瓶这种不可降解物带离海洋生物 仿生鱼可大可小,也可以做的很大 由于对海洋有莫名的喜欢,所以还是很期待仿生鱼的应用的。同时,开发出越强大的仿生机器,就越感慨造物主的伟大!无论我多么热爱科技,也比不上我热爱大自然~ 仿人机器人关键技术研究 (熊蓉,浙江大学智能系统与控制研究所机器人实验室主任)详细链接🔗 当前智能机器人发展若干挑战性问题 (王田苗,北京航空航天大学教授,长江学者特聘教授)详细链接🔗 [AI+专场] ===========================机器学习的可解释性与自动机器学习(胡侠 ,美国德州农工大学数据挖掘实验室主任、计算机学院终身教职系列助理教授)这是三天里我最感兴趣的一场了,上干货! 胡侠教授表示,机器学习要被各行各业普遍接受和应用,前提是要具有可解释性。但是赋予机器学习可解释性是一个非常难的问题。第一,可解释性没有明确的定义,可能是系统的可解释性,也可能是预测结果的可解释性,甚至可能是系统中某一个部分的可解释性。第二,如果做深度学习的可解释工作,现有的深度学习系统千千万,我们没办法对每一个系统都做。第三,让机器学习系统具有可解释性,必须大量HCI、Visualization专家跨学科合作,是一项巨大的挑战。 下面这个视频是一个分类模型的可视化界面,类似决策树/boosting的样子,可以手动增加/删减树的节点,还挺好玩的: 为解决这个问题,胡侠教授提出,将性能强大、不可解释的深度学习系统学到的知识,迁移到性能较弱但可解释的浅度学习系统中。更多机器学习的可解释性信息链接🔗 接下来重点整理下自动机器学习(auto ml)的笔记: 首先介绍什么叫做自动的机器学习(Auto Machine Learning) 目前无论是开发一个机器学习模型,还是招人开发一个模型,经济成本和时间成本都是相当可观的。因此假如有一个框架提供自动的机器学习服务,无需你有超级专业的建模知识,只需要你把数据丢进去,它自动帮你加工数据,自动遍历模型建模,甚至一小时内自动跳出最优的模型结论,岂不是妙哉?这就是 Auto ML 干的事~ 比如像下面这样:下图是最原始、最简单的机器学习系统。我们有一组数据,想知道它是文本还是数值,具体是用Text mining、Classification还是Regression。如果用Classification,效果还不错,系统就会推荐给你。这是最原始的现有产品能实现的功能,给定一些数据后可以推荐相应的系统给大家。 胡侠教授他们的工作内容与成果: 他们挑选了约300个UCI的数据,重新采样形成了4000个数据。然后把能找到的20多个分类的package全部应用到这4000个数据上去,观察效果如何。新的数据进来后,他们就找出矩阵中和新数据最像的Dataset,将这个Dataset上历史表现最好的模型推荐给用户。通过这种方法,将机器学习效果提升了很多。 他们目前正在做的工作是研究怎样做好神经结构的搜索。有了数据后,系统可以自动推荐一个相应的深度学习结构给该数据。在没有资源,没有大量深度学习工程师和数据科学家的情况下,这样一个结构或许可以初步满足初创公司、社会学科和医生的数据探索需求。 第一步,要根据相应模型,通过遗传算法或者强化学习来做。非常耗时耗力。 第二步,有了结构后,还要从头开始训练这个深度学习系统,这样它才能应用到相应的工作中去。 有了深度学习系统的原始结构后,还可以将它变宽、变深、加速,让它的速度更快。 采用了Bayesian Optimization替代传统强化学习和遗传算法,让这一步变得比较快。 所有的学习都是基于上一步,所以第二步也能让速度非常快。他们可以把时间从原始的几天压缩到一个小时内。你给定一个数据,他们很快就能推荐相应的深度学习结构给你。 下图展示了他们一个月前发布的package,Auto-Keras 兴趣使然,看完了auto-keras的全部代码后,像我一样发现还是不够看的话…下面是学界/业界在 auto-machine-learning 上的进展: google’s Cloud AutoML, Google’s Prediction API Microsoft’s Custom Vision, Microsoft’s Azure Machine Learning auto-sklearn,论文链接:Efficient and Robust Automated Machine Learning ClimbsRocks/auto_ml 以及其他提供类似服务的公司:e.g.,BigML.com, Wise.io, SkyTree.com, RapidMiner.com, Dato.com, Prediction.io, DataRobot.com, and Amazon Machine Learning 个人目前最喜欢auto-sklearn,在ML的框架上增加了 meta-learning 和 build-ensemble 两个有意思的模块变成 Auto-ML 框架:也就是说,一般的分类或者回归的机器学习模型即将或者已经实现了低门槛或者零门槛免费的程度。机器学习自动建模的难点还是在数据清洗和特征衍生这些技巧,至于模型的选择和超参数调参已经有比较成熟可用的代码了。所以,已经有开源的代码的话,那些提供建模服务平台的公司为啥还能拉到那么多融资? whatever, 我们的愿景是 人人都可以用得起机器学习系统🙂 [计算机视觉专场] ===========================云、端、芯上的视觉计算 (孙剑,旷视科技首席科学家、研究院院长)蛮喜欢孙剑的,可以种草一下:【详细链接🔗】,推荐❤ ResNet part: 各种net大合照: 计算机视觉研究中的新探索 (林达华,商汤科技联合创始人,港中文-商汤联合实验室主任)【详细链接🔗】,推荐❤ 人脸识别的样本数据基于电影素材,挺有意思的,从而思考出人物之间的关系的信息对人脸识别的作用: 看图说话: 既然AI模型能生成一句话,那么是不是也能生成一段动作?下图展示了他们的一项最新研究,很多AI公司都在做这方面的研究,让AI生成一段生动的舞蹈。下面是一些简单的动作,这些动作都是计算机自动生成的,不是用程序描述出来的: [IoT专场] ===========================海尔的物联网 (赵峰,海尔家电产业集团副总裁、CTO)智慧家庭 4个应用物理空间:智慧客厅、智慧厨房、智慧浴室、智慧卧室 7个解决方案:全屋用电、全屋用水、全屋洗护、全屋安防、全屋娱乐、全屋健康、全屋信息 食联网 物联感知:图像识别、食材识别、温度传感、二维码 人工智能:语音交互、食材管理、菜谱推荐、食品溯源 生态服务:生鲜购物、新闻资讯、视频娱乐、健康管理 衣联网: 衣联生态联盟 衣联标准 APP显示 洗衣机可识别各类衣服 U+ 智慧生活平台, 以 IoT+AI 赋能智慧家庭 业务场景:厨房美食、起居场景、洗浴健康 U+ 智慧生活平台:UHomeOS (FULL/ Compact/ Lite) 数据,物联,生态,交互 终端/用户:机机互联,用户社群 UHomeOS: 设备实时在线,全流程互联互通,实现故障自反馈诊断。(IoT+AI) 可定制交互,分布式、场景化的用户交互方式: 智能音箱 视觉投射 腕投,掌上交互 手机APP 网器屏 电视屏 其它会场的链接🔗 写在最后 建立模型的第一步就是大量的样本数据,而目前就好像是采集数据的阶段,同时数据标注自动化这个功能也凸显的比较实用。很多公司都急着抢占市场,获取数据,将其私有化。的确获取数据有时是付出很大代价的,同时也是科研的第一步。但是其中一位演讲嘉宾提出一个有意思的观点: 数据是人民币,不分享会贬值。新的data在源源不断的产生,不用/不分享的data会贬值。开源的代码早就不是新鲜事了,开源的数据也慢慢开始形成氛围,比如图像领域的ImageNet。 关于算法存在的意义很多算法从你出生前就有了,但是只是从这两年开始火起来而已,还有很多很好用的算法没有火起来,风水轮流转的话,说不定哪天就…所以这就说到了(算法)存在的意义。没有单独的能打遍天下的算法,都是算法之间互相结合去面对实际困难的。同时不要沉迷在算法中不能自拔,要根据现实问题找方法,而不是根据方法找问题。 关于人工智能窃取隐私的说法很多人对于像推荐算法这类的应用有抗拒,比如Facebook之前被用户群起而攻之。我就很想站出来为这些优秀的算法说一句,每个人的id、姓名,这些信息对于算法来说是最最不重要的信息,都会在第一步被剔除。工程师只想知道样本有哪些特征,而特征,就是只是特征,而已。采集数据的时候,只知道有喜欢旅行的这样一个人,而不知道这个人是谁。不是因为你叫“范冰冰”而给你推荐化妆品的广告,而是因为你爱美,有化妆习惯,才会给你推化妆品的广告。如果是一只有购买能力,有化妆习惯,并且有手机,手机上还有社交app的老鼠,也会收到类似的广告的。种草是很花时间和精力的,我就很喜欢优秀的算法推荐出来的,就像知乎的高票回答,淘宝的猜你喜欢,虾米的每日30首。工程师不会打开所有样本用户的摄像头去看他们的隐私,变态才做这样的事。 关于人工智能淘汰人类的说法很多培训机构为了卖课就向人们贩卖焦虑,说人类会因为科技而失业。张钹院士提到说,那些照章办事,不需要任何灵活性的工作才会如此。难道这不是好事吗?科技是在提高我们的生活质量。引用一位更直接的教授的话就是:会用人工智能的代替不会用的。其实很好理解,求生欲旺盛的人们早就会用智能产品,比如智能手机,2018年现在不会用手机的人必定引起生活的不便。求生欲爆炸旺盛的像我这样的人,就去学习人工智能。手机,从某个角度来说也是机器的一种,当手机出来的时候不知道是否引起很多人焦虑。AI is helping people, not replacing them. 三天的行程就像是 a big picture of current AI.提高了一下人生广度😀,也顺便知道同行的伙伴都走到了什么地步~对隔行如隔山的产业成果也一饱眼福。提高广度后,至于深度,那是日积月累的事了。 PS. 门票很贵,死贵,是我霉演唱会的翻倍!所以鞠躬感谢绿米给我这个机会~ 感谢阅读~ Have a happy day😎]]></content>
<tags>
<tag>machine learning</tag>
<tag>CCF-GAIR</tag>
<tag>AI</tag>
<tag>deep learning</tag>
</tags>
</entry>
<entry>
<title><![CDATA[赌博小游戏的开发 - 骰宝]]></title>
<url>%2F2018%2F06%2F13%2F%E8%B5%8C%E5%8D%9A%E5%B0%8F%E6%B8%B8%E6%88%8F%E7%9A%84%E5%BC%80%E5%8F%91-%E9%AA%B0%E5%AE%9D-py%2F</url>
<content type="text"><![CDATA[去过赌场的朋友应该见过这个 —- 骰宝(Sic Bo) 骰子的英文是 dice,有devil’s bonesdice的说法,所以google dice的时候会出现很多这种人型dice~下面是骰宝的台子,上面几乎涵盖所有的玩法: 猜数字 猜单双 比大小 通杀 其他之前帮朋友做了下这个游戏的调研开发,觉得不难。前提是了解其中的规则,参考规则 下面的代码里,是站在庄家的角度写的 money为正的时候,庄家赚钱;money为负的时候,庄家亏钱 Talk is cheap, show me the code.直接上代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307import numpy as npimport matplotlib.pyplot as plt import itertoolsdef cumsum(L): #if L's length is equal to 1 if L[:-1] == []: return L ret = cumsum(L[:-1]) ret.append(ret[-1] + L[-1]) return retchips = 0 # 玩家的筹码量初始值设置#spend_chips = 0money_ALL = [] # 每局的盈亏金额 = 庄家的筹码量money_accum_ALL = []ROUND_ALL = []for ii in range(1,11): money = [] # 每局的盈亏金额 = 庄家的筹码量 money_accum = [] ROUND = [] for i in range(1,801): # 假设每轮的赔率都是固定不变的 要变化的话就自动设置 ROUND.append(i) #----------------------------------------------------- #玩家买柱 #----------------------------------------------------- #chips = chips + 10000 # 累计增加筹码 chips = 10000 # 每局开始有10000筹码 # rest_chips = chips - spend_chips # 买大小 is_buy = np.random.randint(0,2) chips_big_small = np.random.randint(0,chips+1) * is_buy odds_big_small = 1 # 赔率设置 if chips_big_small > 0: guess_big_small = np.random.randint(0,2) # 0:猜小 1:猜大 # 买单双 is_buy = np.random.randint(0,2) chips_odd_even = np.random.randint(0,chips-chips_big_small+1) * is_buy odds_odd_even = 1 # 赔率设置 if chips_odd_even > 0: guess_odd_even = np.random.randint(0,2) # 0:猜双 1:猜单 #买三个一样数字(任意三个一样就行) is_buy = np.random.randint(0,2) chips_num = np.random.randint(0,chips-chips_big_small-chips_odd_even+1) * is_buy if chips_num > 0: guess_A = np.random.randint(1,7) guess_B = guess_A guess_C = guess_A #买 1 1 1 is_buy = np.random.randint(0,2) chips_num_1 = np.random.randint(0,chips-chips_big_small-chips_odd_even-chips_num+1) * is_buy if chips_num_1 > 0: guess_A = 1 guess_B = guess_A guess_C = guess_A #买 2 2 2 is_buy = np.random.randint(0,2) chips_num_2 = np.random.randint(0,chips-chips_big_small-chips_odd_even-chips_num-chips_num_1+1) * is_buy if chips_num_2 > 0: guess_A = 2 guess_B = guess_A guess_C = guess_A #买 3 3 3 is_buy = np.random.randint(0,2) chips_num_3 = np.random.randint(0,chips-chips_big_small-chips_odd_even-chips_num-chips_num_1-chips_num_2+1) * is_buy if chips_num_3 > 0: guess_A = 3 guess_B = guess_A guess_C = guess_A #买 4 4 4 is_buy = np.random.randint(0,2) chips_num_4 = np.random.randint(0,chips-chips_big_small-chips_odd_even-chips_num-chips_num_1-chips_num_2-chips_num_3+1) * is_buy if chips_num_4 > 0: guess_A = 4 guess_B = guess_A guess_C = guess_A #买 5 5 5 is_buy = np.random.randint(0,2) chips_num_5 = np.random.randint(0,chips-chips_big_small-chips_odd_even-chips_num-chips_num_1-chips_num_2-chips_num_3-chips_num_4+1) * is_buy if chips_num_5 > 0: guess_A = 5 guess_B = guess_A guess_C = guess_A #买 6 6 6 is_buy = np.random.randint(0,2) chips_num_6 = np.random.randint(0,chips-chips_big_small-chips_odd_even-chips_num-chips_num_1-chips_num_2-chips_num_3-chips_num_4-chips_num_5+1) * is_buy if chips_num_6 > 0: guess_A = 6 guess_B = guess_A guess_C = guess_A # 买总和数 is_buy = np.random.randint(0,2) chips_sum_num = np.random.randint(0,chips-chips_big_small-chips_odd_even-chips_num-chips_num_1-chips_num_2-chips_num_3-chips_num_4-chips_num_5-chips_num_6+1) * is_buy if chips_sum_num > 0: guess_sum_num = np.random.randint(4,18) if guess_sum_num in [4,17]: # 赔率设置 odds_sum_num = 50 elif guess_sum_num in [5,16]: odds_sum_num = 18 elif guess_sum_num in [6,15]: odds_sum_num = 14 elif guess_sum_num in [7,14]: odds_sum_num = 12 elif guess_sum_num in [8,13]: odds_sum_num = 8 elif guess_sum_num in [9,10,11,12]: odds_sum_num = 6 # 全围 #----------------------------------------------------- # 开局 #----------------------------------------------------- A = np.random.randint(1,7) B = np.random.randint(1,7) C = np.random.randint(1,7) #----------------------------------------------------- # 检验结果 #----------------------------------------------------- money_sum_num = 0 money_num = 0 money_big_small = 0 money_odd_even = 0 kill_money = 0 # 通杀情况1 if chips_sum_num > 0: result_sum_num = A+B+C if A == B & B == C: # 通杀情况下 if guess_sum_num == result_sum_num & guess_sum_num not in [3,18]: # 闲家猜对了 money_sum_num = -(chips_sum_num * 24) else: # 猜错了 kill_money = chips # 通杀,完毕 # 通杀情况2 elif chips_num > 0: if A == B & B == C: # 通杀情况下 if guess_A == A: # 闲家猜中 money_num = -(chips_num * 24) # else: #kill_money = chips elif chips_num_1 > 0: if A == B & B == C & B == 1 : # 通杀情况下 if guess_A == A: # 闲家猜中 money_num = -(chips_num_1 * 150) else: kill_money = chips elif chips_num_2 > 0: if A == B & B == C & B == 2 : # 通杀情况下 if guess_A == A: # 闲家猜中 money_num = -(chips_num_1 * 150) else: kill_money = chips elif chips_num_3 > 0: if A == B & B == C & B == 3 : # 通杀情况下 if guess_A == A: # 闲家猜中 money_num = -(chips_num_1 * 150) else: kill_money = chips elif chips_num_4 > 0: if A == B & B == C & B == 4 : # 通杀情况下 if guess_A == A: # 闲家猜中 money_num = -(chips_num_1 * 150) else: kill_money = chips elif chips_num_5 > 0: if A == B & B == C & B == 5 : # 通杀情况下 if guess_A == A: # 闲家猜中 money_num = -(chips_num_1 * 150) else: kill_money = chips elif chips_num_6 > 0: if A == B & B == C & B == 6 : # 通杀情况下 if guess_A == A: # 闲家猜中 money_num = -(chips_num_1 * 150) else: kill_money = chips # 买总和数 if chips_sum_num > 0: if not (A == B & B == C): if guess_sum_num == result_sum_num: #如果猜中了 money_sum_num = -(chips_sum_num*odds_sum_num) else: money_sum_num = chips_sum_num # 买大小 if chips_big_small > 0: if A+B+C in list(range(1,11)): result_big_small = 0 #结果为 小 else: result_big_small = 1 if guess_big_small == result_big_small: # 如果猜对了 money_big_small = -(chips_big_small*odds_big_small) # 庄家给相应的钱 else: money_big_small = chips_big_small # 买单双 if chips_odd_even > 0: if (A+B+C)%2 == 1: # 结果为 单 result_odd_even = 1 else: result_odd_even = 0 if guess_odd_even == result_odd_even: money_odd_even = -(chips_odd_even*odds_odd_even) else: money_odd_even = chips_odd_even #----------------------------------------------------- # 收钱/给钱 #----------------------------------------------------- # spend_chips = chips_big_small + chips_odd_even + chips_sum_num if kill_money == chips: money.append(kill_money) else: money.append(money_big_small + money_odd_even + money_sum_num) money_ALL.append(money) money_accum = cumsum(money) money_accum_ALL.append(money_accum) ROUND_ALL.append(ROUND) # plt.bar(ROUND,money) # plt.show() # plt.bar(ROUND,money_accum) # plt.show() # print(money) money_ALL = list(itertools.chain.from_iterable(money_ALL))money_accum_ALL = list(itertools.chain.from_iterable(money_accum_ALL))ROUND_ALL = list(itertools.chain.from_iterable(ROUND_ALL))ROUND_ALL = list(range(1,len(ROUND_ALL)+1))plt.bar(ROUND_ALL,money_ALL) plt.show() plt.bar(ROUND_ALL,money_accum_ALL) plt.show() 给出结果图如下:这是每局的盈亏展示,可以看出在第4000局左右的时候,庄家大亏了一把这是盈亏累积展示,在最终,8000局大战之后,还是庄家赢了15万左右 不同国家,不同场子的玩法规则会有不一样,上面的code也不能包含所有规则 另外,python相关文件还是建议以英文命名,标题只是起到编程语言标识的作用 赌场还有很多其他游戏,如果你想败坏兴致,可以像我这么做🙃]]></content>
<tags>
<tag>python</tag>
<tag>game</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Machine Learning笔记 - XGBOOST 教程]]></title>
<url>%2F2018%2F06%2F01%2FXGBOOST%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%2F</url>
<content type="text"><![CDATA[背景说明: XGBOOST,屠榜神器! 全称:eXtreme Gradient Boosting | 简称:XGB XGB作者:陈天奇(华盛顿大学),my icon❤ XGB前身:GBDT(Gradient Boosting Decision Tree),XGB是目前决策树的顶配。 注意!上图得出这个结论时间:2016年3月,两年前,算法发布在2014年,现在是2018年6月,它仍是算法届的superstar🌟! 目前,在所有声名显赫的数据挖掘赛场上(kaggle/天池/…),这个算法无人不知,slay全场。 注: 适用人群:机器学习(数据挖掘)大赛选手 / (准)人工智能工程师 / 算法效果遇到瓶颈的朋友 / … 假设:读者理解回归树算法、泰勒公式、梯度下降法和牛顿法,简单说就是GBDT,顺便,Adaboost也可以了解一下。 When learning XGBoost, be calm and be patient. 因为XGB很屌,所以本文很长,可以慢慢看,或者一次看一部分,it’s ok~ 链接🔗: XGBoost: A Scalable Tree Boosting System【XGB的原著论文】 Introduction to Boosted Trees【天奇大神的ppt】 正文: [1] 算法原理简述(基于上面陈天奇的PPT): (1) Review of key concepts of supervised learning | 监督学习的主要元素 Y值(label标签) 目标函数(Objective Function)= 损失函数(Loss Function)+ 正则化(Regularization) 损失函数表示模型对训练数据的拟合程度,loss越小,代表模型预测的越准。 正则化项衡量模型的复杂度,regularization越小,代表模型模型的复杂度越低。 目标函数越小,代表模型越好。 (2) Regression Tree and Ensemble | 当你谈决策树时你在谈什么Tree Ensemble methods的好处: Very widely used.Almost half of data mining competition are won by using some variants of tree ensemble methods.被大规模的使用,几乎一半的数据挖掘比赛冠军队都在用集合树模型 Invariant to scaling of inputs, so you do not need to do careful features normalization.与输入数据的取值范围无关,所以无需做很细致的特征归一化 Learn higher order interaction between features.能够学习到特征间的高维相关性 Can be scalable, and are used in Industry.工业使用,扩展性好 在这页,模型复杂度(function space)是由所有的回归树决定的。 学习的是fk(树),而不是权重w——体现gradient的思想。 信息增益(Information Gain):决定分裂节点,主要是为了减少损失loss 树的剪枝:主要为了减少模型复杂度,而复杂度被‘树枝的数量’影响 最大深度:会影响模型复杂度 平滑叶子的值:对叶子的权重进行L2正则化,为了减少模型复杂度,提高模型的稳定性 回归树不止用于做回归,还可以做分类、排序等,主要依赖于目标函数的定义 (3) Gradient Boosting (How do we Learn) Bias-variance tradeoff is everywhere偏差与方差的权衡无处不在 The loss + regularization objective pattern applies for regression tree learning (function learning)损失+正则的模式适用于回归树学习 We want predictive and simple functions预测模型的出路在哪里,结果如下: 使用二阶泰勒展开式来近似Loss: 箭头所指的就是XGB的目标函数表达式,Obj目标函数 = 损失函数 + 正则项 + 常数项,是个优秀的表达式,后面会解释 本篇只是提了些基本的概念,其它slice解读请参阅官方介绍或者陈天奇slide学习笔记或者XGBoost算法原理 [2] 参数说明:XGB的参数是目前见过的模型里最多的,面试被问到就瞎了,如果你是第一次看所有的参数,请做好心理准备~下面只列举部分常用参数,所有参数的官方说明文档,请点击XGBoost Parameters (1) General parameters (2) Booster parameters Parameters for Tree Booster Additional parameters for Dart Booster Parameters for Linear Booster and Tweedie Regression(3) Learning Task parameters(4) Command line parameters XGB的下载教程,如果不成功,多google [3] 代码实现:R语言版本(1) 导入数据data0.RData 下载,仅作XGB流程展示,不做数据清洗如果对数据清洗感兴趣,请点击基于R的数据清洗(1)样本数据是RData格式的,是R专有的数据存储格式,好用又不占地方~1234567891011121314# 导入包packages<-c("data.table","xgboost","ggplot2","dplyr")UsePackages<-function(p){ if (!is.element(p,installed.packages()[,1])){ install.packages(p)} require(p,character.only = TRUE)}for(p in packages){ UsePackages(p)}library(data.table)library(xgboost)library(ggplot2)library(dplyr) 导入数据:12setwd("D:/Zhang") # R文件设置路径load("data/data0.RData") # 导入数据 拆分训练集和测试集,转换数据格式:12345678910111213#----------------------------------------------------------# train & test select randomly#----------------------------------------------------------a = round(nrow(data0)*0.8)b = sample(nrow(data0), a, replace = FALSE, prob = NULL)train= data0[b,] # 训练集80%test = data0[-b,] # 测试集20%# 将dataframe格式转换成xgb.DMatrix格式# Y值的列名: 'bad'dtrain <- xgb.DMatrix(data=select(train,-bad)%>%as.matrix,label= train$bad%>%as.matrix) 注:Y值的特征名是‘bad’ (2) 利用 xgb.cv 调参12345678910111213141516171819202122232425262728293031323334353637383940best_param = list()best_seednumber = 1234best_logloss = Infbest_logloss_index = 0# 自定义调参组合for (iter in 1:50) { param <- list(objective = "binary:logistic", # 目标函数:logistic的二分类模型,因为Y值是二元的 eval_metric = c("logloss"), # 评估指标:logloss max_depth = sample(6:10, 1), # 最大深度的调节范围:1个 6-10 区间的数 eta = runif(1, .01, .3), # eta收缩步长调节范围:1个 0.01-0.3区间的数 gamma = runif(1, 0.0, 0.2), # gamma最小损失调节范围:1个 0-0.2区间的数 subsample = runif(1, .6, .9), colsample_bytree = runif(1, .5, .8), min_child_weight = sample(1:40, 1), max_delta_step = sample(1:10, 1) ) cv.nround = 50 # 迭代次数:50 cv.nfold = 5 # 5折交叉验证 seed.number = sample.int(10000, 1)[[1]] set.seed(seed.number) mdcv <- xgb.cv(data=dtrain, params = param, nthread=6, metrics=c("auc","rmse","error"), nfold=cv.nfold, nrounds=cv.nround, watchlist = list(), verbose = F, early_stop_round=8, maximize=FALSE) min_logloss = min(mdcv$evaluation_log[,test_logloss_mean]) min_logloss_index = which.min(mdcv$evaluation_log[,test_logloss_mean]) if (min_logloss < best_logloss) { best_logloss = min_logloss best_logloss_index = min_logloss_index best_seednumber = seed.number best_param = param }}(nround = best_logloss_index)set.seed(best_seednumber)best_seednumber(best_param) # 显示最佳参数组合,到后面真正的模型要用 得到最佳参数组合: (3) 绘制 auc | rmse | error 曲线 123456789101112131415161718192021222324252627282930313233343536373839404142434445#mdcv$evaluation_logxgb_plot=function(input,output){ history=input train_history=history[,1:8]%>%mutate(id=row.names(history),class="train") test_history=history[,9:16]%>%mutate(id=row.names(history),class="test") colnames(train_history)=c("logloss.mean","logloss.std","auc.mean","auc.std","rmse.mean","rmse.std","error.mean","error.std","id","class") colnames(test_history)=c("logloss.mean","logloss.std","auc.mean","auc.std","rmse.mean","rmse.std","error.mean","error.std","id","class") his=rbind(train_history,test_history) his$id=his$id%>%as.numeric his$class=his$class%>%factor if(output=="auc"){ auc=ggplot(data=his,aes(x=id, y=auc.mean,ymin=auc.mean-auc.std,ymax=auc.mean+auc.std,fill=class),linetype=class)+ geom_line()+ geom_ribbon(alpha=0.5)+ labs(x="nround",y=NULL,title = "XGB Cross Validation AUC")+ theme(title=element_text(size=15))+ theme_bw() return(auc) } if(output=="rmse"){ rmse=ggplot(data=his,aes(x=id, y=rmse.mean,ymin=rmse.mean-rmse.std,ymax=rmse.mean+rmse.std,fill=class),linetype=class)+ geom_line()+ geom_ribbon(alpha=0.5)+ labs(x="nround",y=NULL,title = "XGB Cross Validation RMSE")+ theme(title=element_text(size=15))+ theme_bw() return(rmse) } if(output=="error"){ error=ggplot(data=his,aes(x=id,y=error.mean,ymin=error.mean-error.std,ymax=error.mean+error.std,fill=class),linetype=class)+ geom_line()+ geom_ribbon(alpha=0.5)+ labs(x="nround",y=NULL,title = "XGB Cross Validation ERROR")+ theme(title=element_text(size=15))+ theme_bw() return(error) }} auc1xgb_plot(mdcv$evaluation_log[,-1]%>%data.frame,"auc") 训练集与测试集的表现差距有点大,可能出现过拟合 rmse1xgb_plot(mdcv$evaluation_log[,-1]%>%data.frame,"rmse") 训练集与测试集的表现较统一,但是这个数值还是偏高 error1xgb_plot(mdcv$evaluation_log[,-1]%>%data.frame,"error") 测试集的表现非常不稳定,error值偏高 总的来说模型需要进一步调整,但是作为XGB功能以及流程的展示,本篇不做细致调整,继续下一步! (4) 建立模型根据转换后的数据格式dtrain,调参结果的最佳参数组合best_param,最佳迭代次数nround来建模1model <- xgb.train(data=dtrain, params=best_param, nrounds=nround, nthread=6, watchlist = list()) (5) 绘制Importance排序图123456789101112131415importanceRaw <- xgb.importance(feature_names=colnames(dtrain), model = model)xgb.ggplot.importance(importanceRaw) # importance 就是 信息增益# #--------------------------------------------------------------------------------------# # feature selection # 这里可以根据importance设置阈值,进行特征筛选,这是特征筛选的方式之一# cum_impt=data.frame(names=importanceRaw$Feature,impt=cumsum(importanceRaw$Importance))# cum_impt=filter(cum_impt,cum_impt$impt<0.9)# selected_feature<-cum_impt$names# # train=select(train,selected_feature)# dtrain<- xgb.DMatrix(data=select(train,-bad)%>%as.matrix,label= train$bad%>%as.matrix)# # model <- xgb.train(data=dtrain, params=best_param, nrounds=nround, nthread=6, watchlist = list())# #-------------------------------------------------------------------------------------- 上图代表特征的重要性排序,可以设置重要性阈值,进行特征筛选。 (6) 进行预测12dtest=select(test,-bad) # 'bad'是Y值yhat=predict(model,as.matrix(dtest),missing=NA) (7) 保存模型文件1save(model, file = "model/model_xgb.rda") 下次使用时,能直接导入训练好的模型,进行预测。 [4] 代码实现:Python版本xgb的更新迭代特别快,目前在Windows上的安装就很烧脑,希望佛系安装一下不提供源数据,感兴趣的朋友可以去找分类的数据试着跑一下 (1) 拆分数据集任何报错no module的包都请自行pip安装下来123456789101112131415161718192021222324# 导入包import os os.chdir("C:/Users/Yi/Desktop/abc") # 设置文件路径import randomimport pandas as pdimport matplotlib.pyplot as pltimport numpy as npimport xgboost as xgbfrom numpy import sortfrom xgboost import plot_importance,XGBClassifierfrom sklearn.model_selection import train_test_splitfrom sklearn.feature_selection import SelectFromModelfrom sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report, confusion_matrix,mean_squared_errorfrom ggplot import *from sklearn.externals import joblib# split data into X and YX = tmp_df # 特征集,数据请自行提供Y = label_Y # 标签集,数据请自行提供# split data into train and test sets # 拆分数据集X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=7) (2) API接口说明截至 2018/6 ,xgb model 有两个接口,点击接口文件接口文件值得反复阅读熟悉一下,与参数说明一起食用更佳~ XGB Learning API ( import xgboost ) Scikit-Learn API ( from xgboost import XGBClassifier ) (3) XGB调参 方法一: 直接调参,调用 xgboost包 的 XGBClassifier()可以对其参数进行手动修改,default参数如下 方法二: 随机调参,使用 xgb.cv 123456789101112131415161718192021222324252627282930313233343536373839404142best_param = list()best_seednumber = 123best_logloss = np.Infbest_logloss_index = 0dtrain = xgb.DMatrix(X_train, y_train, feature_names = list(X_train))# 自定义调参组合------------------------------------for iter in range(50): param = {'objective' : "binary:logistic", # 目标函数:logistic的二分类模型,因为Y值是二元的 'max_depth' : np.random.randint(6,11), # 最大深度的调节范围 'eta' : np.random.uniform(.01, .3), # eta收缩步长调节范围 'gamma' : np.random.uniform(0.0, 0.2), # gamma最小损失调节范围 'subsample' : np.random.uniform(.6, .9), 'colsample_bytree' : np.random.uniform(.5, .8), 'min_child_weight' : np.random.randint(1,41), 'max_delta_step' : np.random.randint(1,11)} cv_nround = 50 # 迭代次数:50 cv_nfold = 5 # 5折交叉验证 seed_number = np.random.randint(0,100) random.seed(seed_number) mdcv <- xgb.cv(params = param, dtrain=dtrain, metrics=["auc","rmse","error","logloss"], nfold=cv_nfold, num_boost_round=cv_nround, verbose_eval = None, early_stopping_rounds=8, maximize=False) min_logloss = min(mdcv['test-logloss-mean']) min_logloss_index = mdcv.index[mdcv[test-logloss-mean] == min(mdcv[test-logloss-mean])][0] if min_logloss < best_logloss: best_logloss = min_logloss best_logloss_index = min_logloss_index best_seednumber = seed_number best_param = paramrandom.seed(best_seednumber)nround = best_logloss_indexprint('best_round = %d, best_seednumber = %d' %(nround,best_seednumber))print('best_param : ------------------------------')print(best_param) # 显示最佳参数组合,到后面真正的模型要用 方法三:使用 gridsearch 和 cross validation参考 Complete Guide to Parameter Tuning in XGBoost (4) 绘制 train/test 的 auc/rmse/error定义函数1234567891011121314151617181920212223242526272829303132333435363738394041424344def xgb_plot(input,output): history=input train_history=history.iloc[:,8:16].assign(id=[i+1 for i in history.index]) train_history['Class'] = 'train' test_history=history.iloc[:,0:8].assign(id=[i+1 for i in history.index]) test_history['Class'] = 'test' train_history.columns = ["auc_mean","auc_std","error_mean","error_std","logloss_mean","logloss_std","rmse_mean","rmse_std","id","Class"] test_history.columns = ["auc_mean","auc_std","error_mean","error_std","logloss_mean","logloss_std","rmse_mean","rmse_std","id","Class"] his=pd.concat([train_history,test_history]) if output=="auc": his['y_min_auc'] = his['auc_mean']-his['auc_std'] his['y_man_auc'] = his['auc_mean']+his['auc_std'] auc=ggplot(his,aes(x='id', y='auc.mean', ymin='y_min_auc', ymax='y_man_auc',fill=Class)+\ geom_line()+\ geom_ribbon(alpha=0.5)+\ labs(x="nround",y='',title = "XGB Cross Validation AUC") return(auc) if output=="rmse": his['y_min_rmse'] = his['rmse_mean']-his['rmse_std'] his['y_man_rmse'] = his['rmse_mean']+his['rmse_std'] rmse=ggplot(his,aes(x='id', y='rmse.mean',ymin='y_min_rmse',ymax='y_man_rmse',fill=Class))+\ geom_line()+\ geom_ribbon(alpha=0.5)+\ labs(x="nround",y='',title = "XGB Cross Validation RMSE") return(rmse) if output=="error": his['y_min_error'] = his['error_mean']-his['error_std'] his['y_man_error'] = his['error_mean']+his['error_std'] error=ggplot(his,aes(x='id',y='error.mean',ymin='y_min_error',ymax='y_man_error',fill=Class))+\ geom_line()+\ geom_ribbon(alpha=0.5)+\ labs(x="nround",y='',title = "XGB Cross Validation ERROR") return(error) 横坐标是迭代次数,可以观察迭代时是否过拟合 train曲线和test曲线的相差程度,可以侧面反映模型复杂度,检验是否过拟合1xgb_plot(mdcv,'auc') 1xgb_plot(mdcv,'rmse') 1xgb_plot(mdcv,'error') (5) 建模,进行预测,打印评估指标 方法一: 使用 xgboost.train 1234567891011121314151617# 利用上面调参结果: best_parammd_1 = xgb.train(best_param, dtrain, num_boost_round=nround)# 预测dtest = xgb.DMatrix(X_test, feature_names=list(X_test))preds = md_1.predict(dtest)print(mean_square_error(y_test, preds))predictions = [round(value) for value in preds]accuracy = accuracy_score(y_test, predictions)f1_score = f1_score(y_test,predictions)print("Accuracy: %.2f%%" %(accuracy * 100.0))print("F1 Score: %.2f%%" %(f1_score * 100.0))# save modelmd_1.save_model('xgb.model') 方法二: 使用 XGBClassifier() 1234567891011121314151617# 由于 xgb.train 与 XGBClassifier() 有部分参数的名字稍有出入,具体参考API接口文档best_param['learning_rate'] = best_param.pop('eta') # 修改参数字典的某个key名字best_param.update({'colsample_bytree': 1}) # 取消列抽样,修改参数字典的某个valuemd_2 = XGBClassifier(**best_param) # 2个*号,允许直接填入字典格式的parammd_2.fit(X_train, y_train) ypred = md_2.predict(X_test)predictions = [round(value) for value in ypred]# 打印评估指标MSE = mean_squared_error(y_test, predictions)print("MSE: %.2f%%" % (MSE * 100.0)) accuracy = accuracy_score(y_test, predictions)print("Accuracy: %.2f%%" % (accuracy * 100.0))f1_score = f1_score(y_test, predictions)print("F1 Score: %.2f%%" % (f1_score * 100.0)) (6) 绘制Importance排序图1234ax = xgb.plot_importance(md_2, height=0.5)fig = ax.figurefig.set_size_inches(25,20) # 可调节图片尺寸和紧密程度plt.show() (7) 根据Importance进行特征筛选12345678910111213141516171819202122232425262728293031323334353637383940414243# sorted(list(selection_model.booster().get_score(importance_type='weight').values()),reverse = True)importance_plot = pd.DataFrame({'feature':list(X_train.columns),'importance':md_2.feature_importances_})importance_plot = importance_plot.sort_values(by='importance')importance_plot = importance_plot.reset.index(drop=True)thresholds = importance_plot.importancethresholds_valid = np.unique(thresholds[thresholds != 0])for thresh in thresholds_valid: # select features using threshold selection = SelectFromModel(md_2, threshold=thresh, prefit=True) select_X_train = selection.transform(X_train) # train model selection_model = XGBClassifier(**best_param) selection_model.fit(select_X_train, y_train) # eval model select_X_test = selection.transform(X_test) y_pred = selection_model.predict(select_X_test) predictions = [round(value) for value in y_pred] accuracy = accuracy_score(y_test, predictions) print("Thresh=%.4f, n=%d, Accuracy: %.2f%%" % (thresh, select_X_train.shape[1], accuracy*100.0))thresh = 0.034selected_features = list(importance_plot[importance_plot.importance > thresh]['feature'])print('selected features are :\n %s'%selected_features)select_X_train = X_train[selected_features] # 筛选Importance符合阈值的特征集n_features = selected_X_train.shape[1]print('total: %d features are selected' %n_features)selection_model = XGBClassifier(**best_param) selection_model.fit(select_X_train, y_train)select_X_test = X_test[selected_features]y_pred = selection_model.predict(select_X_test)predictions = [round(value) for value in y_pred]accuracy = accuracy_score(y_test, predictions)f1_score = f1_score(y_test, predictions)print("Accuracy: %.2f%%" % (accuracy * 100.0))print("F1 Score: %.2f%%" % (f1_score * 100.0)) 至于是先调参,再做变量筛选,还是先筛选后调参,或是反复调参反复筛选,纯凭个人喜号。 (8) 绘制决策树 先下载graphviz 的 graphviz-2.38.zip,我的是windows,其他系统请自由选择 配置环境变量123# graphviz文件存在的路径配置os.environ["PATH"] += os.pathsep + 'C:/Users/Yi/Anaconda3/envs/release/bin/' # 在引号''这里替换你的dot.exe路径xgb.to_graphviz(md_2, num_trees=0, rankdir='LR') # num_trees的值是第几棵树,0为第一棵,rankdir是树的方向,default是从上到下 (9) 保存模型文件,导入模型文件12345# save modeljoblib.dump(selection_model,'xgb.model')# load modelloaded_model = joblib.load('xgb.model') [5] XGB的优点敲桌子!重点考点!同意义问题:XGB 与 GBDT的区别 损失函数:GBDT是一阶,XGB是二阶泰勒展开 XGB的损失函数可以自定义,具体参考 objective 这个参数 XGB的目标函数进行了优化,有正则项,减少过拟合,控制模型复杂度 预剪枝:预防过拟合 GBDT:分裂到负损失,分裂停止 XGB:一直分裂到指定的最大深度(max_depth),然后回过头剪枝。如某个点之后不再正值,去除这个分裂。优点是,当一个负损失(-2)后存在一个正损失(+10),(-2+10=8>0)求和为正,保留这个分裂。 XGB有列抽样/column sample,借鉴随机森林,减少过拟合 缺失值处理:XGB内置缺失值处理规则,用户提供一个和其它样本不同的值,作为一个参数传进去,作为缺失值取值。XGB在不同节点遇到缺失值采取不同处理方法,并且学习未来遇到缺失值的情况。 XGB内置交叉检验(CV),允许每轮boosting迭代中用交叉检验,以便获取最优 Boosting_n_round 迭代次数,可利用网格搜索grid search和交叉检验cross validation进行调参。GBDT使用网格搜索。 XGB运行速度快:data事先安排好以block形式存储,利于并行计算。在训练前,对数据排序,后面迭代中反复使用block结构。关于并行,不是在tree粒度上的并行,并行在特征粒度上,对特征进行Importance计算排序,也是信息增益计算,找到最佳分割点。 灵活性:XGB可以深度定制每一个子分类器 易用性:XGB有各种语言封装 扩展性:XGB提供了分布式训练,支持Hadoop实现 共同优点: 当数据有噪音的时候,树Tree的算法抗噪能力更强 树容易对缺失值进行处理 树对分类变量Categorical feature更友好 XGB实在太强大 实时在更新,目前的总结只是利用目前的资源 未来会发展成什么样,谁也猜不到。 参考: 官方使用手册 Awesome XGBoost R+python︱XGBoost]]></content>
<tags>
<tag>machine learning</tag>
<tag>python</tag>
<tag>xgboost</tag>
<tag>algorithm</tag>
<tag>boosting</tag>
<tag>gradient</tag>
<tag>R</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Machine Learning笔记 - 基于R的数据清洗(1)]]></title>
<url>%2F2018%2F05%2F25%2F%E5%9F%BA%E4%BA%8ER%E7%9A%84%E6%95%B0%E6%8D%AE%E6%B8%85%E6%B4%97%EF%BC%881%EF%BC%89%2F</url>
<content type="text"><![CDATA[当算法逐渐框架化,变成调参的把戏,数据清洗 就成为了所谓的数据挖掘的精髓,是你与别人的模型拉开差距的地方,也是你建模功力的最佳展示。如何洗数据?当然不是立白洗衣液,雕牌洗衣皂之类的。 数据清洗是一个漫长、耗时、经验积累的过程,本文包括: 导入数据 理解特征、数据类型 缺失值处理 数据类型的统一性处理 分类变量的处理(unary | binary | nomial | categorical | ordinal) 连续变量的处理(interval) 排序不分先后,有些步骤会重复出现比如观察缺失值,缺失值填充等 1. 导入数据train.csv 下载本文的样本数据为kaggle入门赛Titanic的数据,地球人都懂的……吧? 12345678910111213# 导入包packages<-c("ggplot2","dplyr","varhandle")UsePackages<-function(p){ if (!is.element(p,installed.packages()[,1])){ install.packages(p)} require(p,character.only = TRUE)}for(p in packages){ UsePackages(p)}library(ggplot2)library(dplyr)library(varhandle) 以下的数据清洗方法、思路包含却不仅限于这个数据样本,按照整体分析框架会拓展到其他常用的特征1234# 我将下载的数据放在"D:/1_kaggle/titanic/data"的路径下setwd("D:/1_kaggle/titanic") # 设置路径train = read.csv('data/train.csv') # 导入csv格式的数据 2. 理解特征、数据类型123456789101112131415161718192021222324ncol(train) # 字段数量 列数# 12nrow(train) # 样本数量 行数# 891colnames(train) # 字段名 列名# "PassengerId" "Survived" "Pclass" "Name"# "Sex" "Age" "SibSp" "Parch" # "Ticket" "Fare" "Cabin" "Embarked" cbind(apply(train,2,function(x)length(unique(x))),sapply(train,class)) # 获取每列的数据种类数,和 数据类型# PassengerId "891" "integer"# Survived "2" "integer"# Pclass "3" "integer"# Name "891" "factor" # Sex "2" "factor" # Age "89" "numeric"# SibSp "7" "integer"# Parch "7" "integer"# Ticket "681" "factor" # Fare "248" "numeric"# Cabin "148" "factor" # Embarked "4" "factor" 训练集中乘客的特征有:PassengerId、Pclass、Name、Sex、Age、SibSp、Parch、Ticket、Fare、Cabin、Embarked 12个字段/特征,12列,891行 integer | numeric 为连续变量/数值型特征 factor 为分类变量/离散型的特征,一般需要额外处理才能训练 3. 缺失值处理(1)观察缺失值比例123456789101112131415161718192021# 建立观察缺失值比例的函数 na.plotna.plot=function(data){ missing1=sapply(data,function(x)sum(x == '')/nrow(data)) missing2=sapply(data,function(x)sum(sum(is.null(x)), sum(is.na(x)))/nrow(data)) if(sum(is.na(missing1))>0){ missing1[is.na(missing1)] = 0 } missing = missing1 + missing2 print(missing) missing=missing[order(missing,decreasing = T)] nadata=missing[missing>0] na_df=data.frame(var=names(nadata),na=nadata,row.names = NULL) ggplot(na_df)+ geom_bar(aes(x=reorder(var,na),y=na),stat='identity', fill='red')+ labs(y='% Missing',x=NULL,title='Percent of Missing Data by Feature') + coord_flip(ylim = c(0,1)) }# 调用函数na.plot(train) 字段 Cabin/ Age/ Embarked 有缺失值 缺失值缺少越好,没有缺失值最好 缺失值存在于DataFrame的形式: NA NULL ‘’ 123456789101112# 以Cabin为例train$Cabin[is.na(train$Cabin)] # 筛选出Cabin为NA的缺失值Cabin# train$Cabin[!is.na(train$Cabin)] # 反逻辑train$Cabin[is.null(train$Cabin)] # 筛选出Cabin为NULL的缺失值Cabin # train$Cabin[!is.null(train$Cabin)]# 反逻辑train$Cabin[train$Cabin == ''] # 筛选出Cabin为''的缺失值Cabin# train$Cabin[train$Cabin != ''] # 反逻辑filter(train,!is.na(Cabin)&!is.null(Cabin)&(Cabin != ''))$Cabin # 筛选出非缺失值的Cabin值 这样做数据清洗不免有些繁琐,如果逻辑合理,可以把NULL值和’’值都处理成NA值123456# 三种缺失值的区别在于length长度length(NA) == 1length(NULL) == 0length('') == 1 (2)当高缺失值占比出现时(例:missing proportion > 95%),一般考虑删除该特征1234567891011121314151617181920# 建立删除缺失值占比高于某比例的特征的函数# 下面的函数设置的阈值是 0.75na.drop=function(data){ missing1=sapply(data,function(x)sum(x == '')/nrow(data)) missing2=sapply(data,function(x)sum(sum(is.null(x)), sum(is.na(x)))/nrow(data)) if(sum(is.na(missing1))>0){ missing1[is.na(missing1)] = 0 } missing = missing1 + missing2 missing=cbind(colnames(data),as.numeric(missing)[!is.na(as.numeric(missing))])%>%as.matrix() print(missing) valid=missing%>%as.data.frame%>%filter(missing[,2]<0.75) # 缺失值占比阈值设置 valid$V1<-unfactor(valid$V1) data=data%>%select(valid$V1)%>%as.data.frame()}train=na.drop(train) # 实施删除高缺失值特征na.plot(train) # 观察是否被删除 Cabin缺失77%,将被删除 (3)重要特征 & 高缺失值占比:将该特征转换成binary特征,有数值为1,缺失值为01234567891011121314151617181920212223train = read.csv('data/train.csv') #恢复原df# 建立函数:筛选出高缺失值占比的特征na.toBinary=function(data){ missing1=sapply(data,function(x)sum(x == '')/nrow(data)) missing2=sapply(data,function(x)sum(sum(is.null(x)), sum(is.na(x)))/nrow(data)) if(sum(is.na(missing1))>0){ missing1[is.na(missing1)] = 0 } missing = missing1 + missing2 missing=cbind(colnames(data),as.numeric(missing)[!is.na(as.numeric(missing))])%>%as.matrix() print(missing) toBinary=missing%>%as.data.frame()%>%filter(missing[,2]>0.75 & missing[,2]<1) # 缺失值占比阈值设置,这里是选取缺失值比例大于0.75,小于1的字段,仅仅作为筛选出Cabin字段 toBinary$V1<-unfactor(toBinary$V1) print(toBinary$V1)}na.toBinary(train)# "Cabin"# 假设Cabin为重要信息,对缺失值进行二值化train$Cabin = as.numeric(!is.na(train$Cabin)&!is.null(train$Cabin)&(train$Cabin != '')) 这里只是拿Cabin做个例子,实际上并不会把这个信息二值化 当出现重要特征 & 高缺失值占比时,才进行这个处理 (4)缺失值填充 缺失值填充有很多方法: 数值型特征:可以用 均值/最大值/最小值/众数 填充 时间序列特征:例如苹果今天的价格缺失,可以用昨日的价格填充 将缺失值归为一类:可以用不曾出现也不会出现的值,比如:缺失年龄用 999 填充,缺失体重用 -1 填充 缺失比例小的连续变量:可用有数值的数据进行对缺失值的回归预测填充,例:班上某同学的身高 重要特征 & 高缺失值占比:将缺失值二值化,在上面已经举例说明 其他:可以用任何合理的逻辑进行填充 (数值型特征)均值/最大值/最小值/众数的获取123456789101112131415161718192021222324# 以 Age 为例tmp_Age = filter(train,!is.na(Age)&!is.null(Age)&(Age != ''))$Age # 筛选出非空年龄值mean(tmp_Age)# 29.69912max(tmp_Age)# 80min(tmp_Age)# 0.42# 众数没有直接可以调用的function,需要自己写# create getmode functiongetmode<-function(v){ uniqv<-unique(v) uniqv[which.max(tabulate(match(v,uniqv)))]}getmode(tmp_Age)# 24# 将Age的缺失值以众数填充train$Age[is.na(train$Age)] = getmode(tmp_Age) 4. 数据类型的统一性处理数据类型有两种属性: 在数据里展示的类型 在实际意义中的类型 当两种类型不统一时,需要将其统一,或者做相应标识 例: Pclass,舱位等级这个特征在数据集中以 1/2/3 的整数展示,显示类型为”integer”,为连续变量,但按字面解释这个特征实际上属于categorical分类变量,等级1/等级2/等级3,意义上类型应该是”factor”,两种属性不统一。 SibSp特征描述乘客的兄弟姐妹或者配偶数量,显示类型为”integer”,字面类型也是数值型,连续变量,两种类型统一。 数据类型应该统一如下: 连续变量:Age、SibSp、Parch、Fare(已经全部为数值型变量) 分类变量:PassengerId、Pclass、Name、Sex、Ticket、Cabin、Embarked(有数值型有离散型) 5. 分类变量的处理分类变量会出现多种情况:(1)情况:分类信息需要提取在Name中,有Mr/Mrs/Miss等称呼信息可以判别乘客的性别,虽然在这里Sex性别的信息未缺失,但是在结果出来前所有的信息都不能轻易丢弃而且,按照Miss/Mrs可以大概预测缺失的年龄,对于名字中有Mrs的乘客的缺失年龄填充就不会出现10岁之类的123456789101112131415161718192021train = read.csv('data/train.csv')which(grepl('Mrs.', train$Name)) # 筛选出Mrs乘客的Indextrain$Name[which(grepl('Mrs.', train$Name))] # 筛选出Mrs乘客的名字,这部分人的性别可以标识为 女性,在这里暂不做改动which(grepl('Mrs.', train$Name[is.na(train$Age)])) # 筛选出年龄为缺失值的Mrs乘客的Index# 筛选出年龄为缺失值的Mrs乘客的年龄,下一步进行赋值train$Age[is.na(train$Age)][which(grepl('Mrs.', train$Name[is.na(train$Age)]))] # 用无缺失值的Mrs群体的年龄的均值 来填充 Mrs群体的缺失年龄# 这样比直接用全体样本的年龄均值来填充缺失的样本年龄要合理一些train$Age[is.na(train$Age)][which(grepl('Mrs.', train$Name[is.na(train$Age)]))] = mean(train$Age[!is.na(train$Age)][which(grepl('Mrs.', train$Name[!is.na(train$Age)]))])# 同理可以对 Miss群体和 Mr群体进行年龄填充train$Age[is.na(train$Age)][which(grepl('Miss.', train$Name[is.na(train$Age)]))] = mean(train$Age[!is.na(train$Age)][which(grepl('Miss.', train$Name[!is.na(train$Age)]))])train$Age[is.na(train$Age)][which(grepl('Mr.', train$Name[is.na(train$Age)]))] = mean(train$Age[!is.na(train$Age)])train$Age[is.na(train$Age)] = mean(train$Age[!is.na(train$Age)])# 将年龄整数化train$Age = as.integer(train$Age) (2)情况:分类的类数level太多 ID类型: PassengerId:一个样本为一类,每个样本的ID不一样,无效信息直接删除 身份证ID:与单纯的Index字段不一样,不能直接删除,能从里面提取出生年月日,年龄,性别,户籍省份等,信息提取完毕后可以删除 userID:有些用户ID里也包含用户的注册信息,比如注册年月日等,需要对数据敏感 多类别少样本 类型: 合并类别:双十一购物者的省份信息,在江浙沪有爆炸性的收件数量,而在西藏、新疆近10个区域等地的收件数量很少,可以把这10个地区合并为一类:偏远地区 (3)情况:分类的类数level太少 单类别:所有样本都属于一个类别,无效类别信息,删除特征 (4)情况:连续变量离散化/分箱 比如Age:可以把连续的年龄数值划分为:少年/青年/中年/老年,在决策树中这种操作会比较常见 处理方法:(1)onehot 独热编码处理one-hot encoding就是用h个变量来代表这h个level,比如3个level的变量就表示成100,010,001 这里以Cabin、Embarked为例,进行onehot处理123456789101112131415161718192021222324252627282930313233train = read.csv('data/train.csv')# Cabin本身是由字母与数字组成的# 字母更能代表舱位的一定信息,因此对Cabin做如下处理# 对于有值的Cabin:仅保留字母信息# 对于缺失值的Cabin:用 NA 作为一类填充train$Cabin = ifelse(train$Cabin != "",substr(gsub('[0-9]', '', train$Cabin), 1, 1), 'NA')unique(train$Cabin) # 处理后Cabin的数值种类# NA C E G D A B F T # 保留Embark的字母信息,缺失值用NA填充train$Embarked = ifelse(train$Embarked != "", substr(train$Embarked, 1, 1), 'NA')unique(train$Embarked) # 处理后Embark的数值种类# S C Q NA# 只有class类型为factor的特征才能做model.matrix处理,需要提前把character类型转换为factor类型features = c('Cabin','Embarked') #需要做转换的特征名称for (f in features){ if( (class(train[[f]]) == "character") || (class(train[[f]]) == "factor")) { levels = unique(train[[f]]) train[[f]] = factor(train[[f]], level = levels) }}# onehot处理trainMatrix <- model.matrix(~ Cabin + Embarked, data=train, contrasts.arg = lapply(train[,c('Cabin','Embarked')], contrasts, contrasts=FALSE))trainMatrix <- trainMatrix[,-1] Cabin变成length(unique(train$Cabin)) = 9 个onehot特征 Embark变成length(unique(train$Embarked)) = 4 个onehot特征 (2)dummy 虚拟变量编码处理dummy encoding就是把一个有h个level的变量变成h-1个变量,比如3个level的变量就表示成成10,01,或00 6. 连续变量的处理当分类变量都变成 0/1 的数字,而连续变量(比如年龄,是0~100的分布)的值域远远大于0~1,难免显得不公平。这个说法是有科学依据的,有兴趣可以手动推导一下。因此一般会对连续变量进行 归一化(Regularization) | 标准化(Standardization) | 去中心化 | …(基本上是同一类思想,暂且都称为标准化) 标准化有以下几种处理方式:emmm….暂时懒得再打一遍,直接上图,希望能看的清楚 这里以Fare为例,进行第二种:最大-最小化 | max-min 处理1train$Fare = (train$Fare-min(train$Fare))/(max(train$Fare)-min(train$Fare)) 这样票价Fare的值域就处于0~1之间了 清洗这个dataframe不是目的本文的目的是,当遇到类似的脏数据时,知道怎么去清洗 本文只是展示了数据清洗的一个大概框架,具体的清洗方法会根据不同的脏数据做不同的处理比如,如何从身份证ID获取生日/性别等信息 在【基于R的数据清洗(2)】中,将会涉及一些有代表性的特征的处理,为特征工程模块做铺垫 作业:RMarkdown的安装与使用]]></content>
<tags>
<tag>machine learning</tag>
<tag>R</tag>
<tag>data cleaning</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CNN笔记 - LeNet5结构详解]]></title>
<url>%2F2018%2F05%2F02%2FCNN%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-LeNet5%E7%BB%93%E6%9E%84%E8%AF%A6%E8%A7%A3%2F</url>
<content type="text"><![CDATA[学习卷积神经网络(CNN)是学习TensorFlow绕不开的一道坎CNN的学习计划是:【算法结构详解 + Tensorflow代码实现】 LeNet5 AlexNet VGG GoogleNet ResNet ====== 概念铺垫 ======CNN有几个重要的点:局部感知、参数共享、池化。 局部感知每个神经元其实没有必要对全局图像进行感知,只需要对局部进行感知,然后在更高层将局部的信息综合起来就得到了全局的信息。 参数共享参数共享是减少参数的有效办法。具体做法是,在局部连接中隐藏层的每一个神经元连接的是一个 10×10 的局部图像,因此有10×10 个权值参数,将这 10×10 个权值参数共享给剩下的神经元。 单核单通道卷积单个卷积核示意: 步长 Stride = 15×5 Image ——> 3×3 kernel ——> (5-3+1)×(5-3+1) convolved feature map 多核单通道卷积一个卷积核提取得到的特征是不充分的。假设有k个卷积核,那么可训练的参数的个数就变为了k×10×10。注意没有包含偏置参数。每个卷积核得到一个Feature Map。卷积的过程为特征提取的过程,多核卷积中,隐层的节点数量为: k×(1000-100+1)×(1000-100+1) ,对于下图的手写数字灰度图,做单通道卷及操作: 多核多通道卷积当图像为RGB或ARGB(A代表透明度)时,可以在多通道进行卷积操作,或者对于堆叠卷积层来说, pooling 层之后可以继续接下一个 卷积层,对 pooling 层多个 Feature Map 的操作即为多通道卷积,下图为 两个卷积核在ARGB四通道上进行卷积操作,在生成 对应的 Feature Map 时, 这个卷积核对应4个卷积模板(这一个卷积核对应的四个模板都不一样),分别用4种不同的颜色表示,Feature Map 对应的位置的值是由四核卷积模板分别作用在4个通道的对应位置处的卷积结果相加然后取激活函数得到的,所以在四通道得到2通道的过程中,参数数目为 4×2×2×2个,其中4表示4个通道,第一个2表示生成2个卷积核,最后的2×2表示卷积核大小。见下图: 池化 pooling池化过程示意: 正文:LeNet5主要用于手写数字的自动识别output输出类别共10个,为数字0,1,2,…,9 LeNet的8层结构:输入层-C1卷积层-S2池化层-C3卷积层-S4池化层-C5卷积层-F6全连接层-输出层 LeNet的8层全过程图: 下图为对应每层的参数个数parameters和连接点个数connections计算流程图,每个feature map下有该层的计算公式: == 输入层 == 1张32×32的图片 == C1卷积层 == 单通道,6个卷积核,得到6个feature maps kernal size: 5×5 feature map size: (32-5+1)×(32-5+1)= 28×28 parameters: 6×(5×5+1) 5×5为卷积模板参数,1为偏置参数 connections: 6×(5×5+1) ×28×28 == S2池化层 == 6个池化核,得到6个feature maps kernal size:2×2 feature map size: (28/2)×(28/2)= 14×14 parameters: 6×(1+1) parameters计算过程:2×2 单元里的值相加然后再乘以训练参数w,再加上一个偏置参数b(每一个feature map共享相同w和b)(这个地方和之前自己想的不太一样) connections: 6×(2×2+1) ×14×14 下图为卷积操作与池化的示意图: == C3卷积层 == 多通道:14个通道,16个卷积核,得到16个feature maps kernal size:5×5 feature map size: (14-5+1)×(14-5+1)= 10×10 parameters: 6×(3×5×5+1) + 6×(4×5×5+1) + 3×(4×5×5+1) + 1×(6×5×5+1) 注意此处C3并不是与S2全连接而是部分连接,见下图 connections: 6×(3×5×5+1) + 6×(4×5×5+1) + 3×(4×5×5+1) + 1×(6×5×5+1) ×10×10 为什么采用部分连接? Dropout的由来首先部分连接,可计算的参数就会比较少其次更重要的是它能打破对称性,这样就能得到输入的不同特征集合以第0个feature map描述计算过程:用1个卷积核(对应3个卷积模板,但仍称为一个卷积核,可以认为是三维卷积核)分别与S2层的3个feature maps进行卷积,然后将卷积的结果相加,再加上一个偏置,再搞个激活函数就可以得出对应的feature map了 == S4池化层 == 16个池化核,得到16个feature maps kernal size:2×2 feature map size: (10/2)×(10/2)= 5×5 parameters: 16×(1+1) connections: 16×(2×2+1) ×5×5 == C5卷积层 == 120个卷积核,得到120个feature maps kernal size:5×5 每个feature map的大小都与上一层S4的所有feature maps进行连接,这样一个卷积核就有16个卷积模板 feature map size: (5-5+1)×(5-5+1)= 1×1 这样刚好变成了全连接,但是我们不把它写成F5,因为这只是巧合 parameters: 120×(16×5×5+1) connections: 120×(16×5×5+1) ×1×1 == F6全连接层 == Full Connection parameters: 84×(120×1×1+1) connections: 84×(120×1×1+1) ×1×1 == 输出层 == 得到分类结果,例:结果为数字 5 代码实现:12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576import osprint(os.getcwd()) #显示当前路径os.chdir("D:/test/") #在引号内填入你想放代码和数据的路径import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/",one_hot = True) #读取数据sess = tf.InteractiveSession() def weight_variable(shape): # 权值初始化设置 initial = tf.truncated_normal(shape,stddev=0.1) return tf.Variable(initial) def bias_variable(shape): # 偏置bias初始化设置 initial = tf.constant(0.1,shape = shape) return tf.Variable(initial) def conv2d(x,W): return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME') # 卷积strides=[首位默认为1,平行步长=1,竖直步长=1,尾位默认为1] def max_pool_2x2(x): return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME') # ksize=[1,2,2,1] 池化核size: 2×2 # 池化strides=[首位默认为1,平行步长=2,竖直步长=2,尾位默认为1]# placeholder:等待输入数据,x为占位符,接受型号为float32的数据,输入格式为矩阵[None,784]x = tf.placeholder(tf.float32,[None,784]) #784 = 28×28 只对输入矩阵的列数有要求y_ = tf.placeholder(tf.float32,[None,10]) x_image = tf.reshape(x,[-1,28,28,1]) # [batch=-1, height=28, width=28, in_channels=1]# Conv1 Layer 卷积层 W_conv1 = weight_variable([5,5,1,32]) # [5×5卷积核, in_channels=1, out_channels=32=卷积核个数]b_conv1 = bias_variable([32]) # [32=卷积核个数=bias个数]h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1) # 激活函数:reluh_pool1 = max_pool_2x2(h_conv1) # 池化方式:max pool 取最大值 # Conv2 Layer W_conv2 = weight_variable([5,5,32,64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2) # 激活函数:reluh_pool2 = max_pool_2x2(h_conv2) W_fc1 = weight_variable([7*7*64,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) keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob) 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) # 损失函数loss function: 交叉熵cross_entropycross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),reduction_indices=[1])) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) # 优化方法:AdamOptimizer| 学习速率:(1e-4)| 交叉熵:最小化 correct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1)) # tf.equal返回布尔值 | tf.argmax(y_,1):数字1代表最大值accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) tf.global_variables_initializer().run() for i in range(20000): batch = mnist.train.next_batch(50) # 喂入训练集的数据 if i % 1000 == 0: # 批量梯度下降,把1000改成1:随机梯度下降 train_accuracy = accuracy.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}) # train accuracy: accuracy.eval print("step %d, training accuracy %g"%(i,train_accuracy)) train_step.run(feed_dict={x:batch[0],y_:batch[1],keep_prob:0.5}) # test accuracy: accuracy.eval print("test accuracy %g"%accuracy.eval(feed_dict={x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0})) 参考: 一个好玩的做数字分类的网站 另一个好玩的卷积网站 LeNet5介绍 别人的笔记看的再多也不如亲自看一遍原著论文呀~❤ LeNet论文链接]]></content>
<tags>
<tag>CNN</tag>
<tag>LeNet</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Anaconda多环境多版本python配置]]></title>
<url>%2F2018%2F04%2F25%2FAnaconda%E5%A4%9A%E7%8E%AF%E5%A2%83%E5%A4%9A%E7%89%88%E6%9C%ACpython%E9%85%8D%E7%BD%AE%2F</url>
<content type="text"><![CDATA[尝试过 pycharm / jupyter notebook / spyder在python的学习初期,寻寻觅觅后钟情于Anaconda的spyder适合做数据分析,可以选中运行。先学的R语言接触的Rstudio,看见spyder就是格外亲切,与matlab的环境都是同一风格的。 为什么都推荐Anaconda做python的学习环境?可自定义设置R或python的版本环境,且可以随意切换。 python学习中免不了遇到一会这个操作只能在2.7下施展,一会那个框架只能在3.5下搭建的情形,所以在机子上拥有多环境版本的python是必备技能了。 以下是Anaconda多环境配置正文 1. 下载Anaconda👉传送门傻瓜式安装步骤,在路径设置时选中环境变量配置,或者安装完自己配置。我选的3.6win版本。 2. 打开Anaconda Navigator左边一列有 Home/ Environments/ Projects/ Learning/ Community选中Environments default环境:base(root) 另外增加的3.5环境:python35 假设需求是搭建3.5环境 点击最下一排的create 出现create new environment对话框 python35 作为名字标识,Name不能以数字开头 可选python或者R,这里选中python 在版本下拉框选择3.5 点击 create,等待一会就在Environments出现如图python35环境 注:加密环境会导致不能用Anaconda Navigator注:增加环境配置步骤也可在Anaconda Prompt实现12345打开Anaconda Prompt 使用命令conda info -e查看当前系统下的环境名称 使用命令conda create --name python35 python=3.5创建一个名为python35的新环境,并指定python版本为3.5,如果仅用python=3,则会安装最新的3.x版本 安装完成后通过activate python35激活新环境 此时查看python版本python --version即为python3的版本 3. 在新环境配置 spyder/ jupyetr/… 打开prompt激活新环境 输入 activate name(环境名)这里输入activate python35不用配置任何环境变量,在最前方的括号里(python35)显示已经在3.5环境的路径下 配置spyder输入 conda install spyder也可下载jupyter notebook或者其他等待安装完成,在你的菜单下也会出现spyder(python35),如下图这样可以直接在这个环境下敲代码👍 install packages需要的包可以在navigator上搜索下载,或者在prompt上pip install… 退出当前python环境当前工作环境的路径切换到系统根目录:prompt输入:deactivate 到此环境就搭完啦~ 可以安心的在python上大展身手啦! 参考:Anaconda多环境多版本python配置指导如何在多版本anaconda python环境下转换spyder?]]></content>
<tags>
<tag>python</tag>
<tag>anaconda</tag>
<tag>spyder</tag>
<tag>environment</tag>
</tags>
</entry>
<entry>
<title><![CDATA[如何搭建自己的个人网站(下)]]></title>
<url>%2F2018%2F04%2F20%2F%E5%A6%82%E4%BD%95%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99%EF%BC%88%E4%B8%8B%EF%BC%89%2F</url>
<content type="text"><![CDATA[本篇涉及Hexo个人网站的域名绑定、添加评论功能、访问次数统计设置等教程。适宜人群:有追求的博主们(主题Theme未使用Next的博主们也请进) 域名绑定根据上篇教程,目前默认的域名还是username.github.io,但印象中个人网站好像都是www.name.com格式的?怎么样想换吗?首先✋,你得有___? 当然是域名啦! 买域名你得买一个域名。xx云都能买,我在腾讯云买的。(请给我广告费!@腾讯云)我实在太喜欢Karlie Kloss,她的一个网站kodewithklossy.com那么我的肯定就是codewithzhangyi.com😜 实名认证审核买好域名之后,打开域名服务,可以看见你刚买的域名记录。此时你需要做实名认证,很重要,超过有效期(大概3-5天)未认证域名将被锁定。 备案如果服务器是内地的就需要备案。Hexo网站的服务器是海外的因此可以跳过这步。 添加解析记录认证成功后的域名才能被做解析。认证需要3-5个工作日。我的大概半天就OK了。当解析状态变成【正常解析】时,点击域名记录最右侧操作的【解析】添加解析:解析记录设置两个:www和@,线路默认就ok(1) @:记录类型选A,A记录的就是ip地址,github(官方文档)提供了两个IP地址,192.30.252.153和192.30.252.154,这两个IP地址为github的服务器地址,两个都要填上(2) www:记录类型选CNAME,CNAME记录值填你的github博客网址,如我的是yzhang1270.github.io 本地设置修改这些全部设置完成后,此时你并不能根据申请的域名访问你的博客。接着你需要做的是在hexo根目录的source文件夹里创建CNAME文件,不带任何后缀,里面添加你的域名信息,如:codewithzhangyi.com。实践证明如果此时你填写的是www.codewithzhangyi.com那么以后你只能用www.codewithzhangyi.com访问,而如果你填写的是codewithzhangyi.com,那么用www.codewithzhangyi.com和codewithzhangyi.com访问都是可以的。重新hexo g,并发布即可用新的域名访问。 访问出现404的原因可能是:(1)绑定了个人域名,但是域名解析错误。(2)域名解析正确但你的域名是通过国内注册商注册的,你的域名因没有实名制而无法访问。(3)配置没问题的情况下,换个浏览器试试。(4)下载的hexo有问题,重新下载。 添加评论模块 在添加评论这个设置上费了点时间,因为整顿,评论服务挂了一大片,请各位寻找教程的时候重点看时间,2017年及之前的文章就没有多大的借鉴意义,包括这篇教程也是有时限性的,谁能跟的上变化呢。 (1) 多说 - 最多用户使用的评论,但遗憾2017年6月将暂定服务;不建议新用户使用,但为旧用户保留,也感谢多说一路的陪伴;(2) 网易云跟帖 - 网易提供的评论组件,功能比较简单,性能优秀;管理后台在查询上还不算特别智能,但足够普通用户使用;(3) 畅言 - 搜狐提供的评论组件,功能丰富,体验优异;但必须进行域名备案。只要域名备过案就可以通过审核。(4) Disqus - 国外使用较多的评论组件。万里长城永不倒,一枝红杏出墙来,你懂的。以上评论模块应该大家都知道,多说和网易云跟帖没有了,畅言要备案,对于对于挂靠在GitHub的博客非常的不友好,放弃!Disqus,不希望自己的博客,可以不分国界!也放弃! 踩坑总结:使用Gitment评论服务Gitment 是作者imsun实现的一款基于 GitHub Issues 的评论系统。支持在前端直接引入,不需要任何后端代码。可以在页面进行登录、查看、评论、点赞等操作,同时有完整的 Markdown / GFM 和代码高亮支持。尤为适合各种基于 GitHub Pages 的静态博客或项目页面。 注册 OAuth Application注册一个新的OAuth Application,其他内容可以随意填写,但要确保填入正确的 callback URL(如 https:// yzhang1270.github.io)这个真的很重要!!!创建成功后,你会得到一个 client ID 和一个 client secret,这个将被用于之后的用户登录。 引入 Gitment将下面的代码添加到你的D:\username.github.io\themes\typing\layout_partial\after-footer.ejs:【提示】:我这里的主题是typing,在typing里是自带gitment的。 123456789101112131415<div id="container"></div><link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css"><script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script><script>var gitment = new Gitment({ id: '页面 ID', // 可选。默认为 location.href 这里有机关,后面会再讲到!😎 owner: '你的 GitHub Name', //比如我的叫YZHANG1270 repo: '存储评论的 repo', //比如我的叫YZHANG1270.github.io oauth: { client_id: '你的 client ID', //比如我的328*********** client_secret: '你的 client secret', //比如我的49ce*********************** },})gitment.render('container')</script> 可选:在主题的_config.yml中配置好全局参数:同时也要在脚本修改指定地址。 初始化评论页面发布后,你发现评论有个error:此时,你点击Login用自己账户登陆,再刷新页面就有初始化按键,点击初始化即可:正常情况下,只要用GitHub账户登陆即可发布评论啦: 在评论功能设置这里踩了几个坑,进行总结一下😝 Gitment评论功能踩坑总结 owner: ‘Your GitHub ID’1owner: '你的 GitHub ID' 可以是你的GitHub用户名,也可以是GitHub id,建议直接用GitHub用户名就可以。获取GitHub id的方法: https:// api.github.com/users/你的账户名 Error: Not Foundowner或者repo配置错误了,注意名字和仓库名字的大小写。 Error: Comments Not Initialized(1)在注册OAuth Application这个步骤中,给Authorization callback URL指定的地址错了(2)还没有在该页面的Gitment评论区登陆GitHub账号(3)https://github.com/imsun/gitment/issues/95 Error:validation failed这个真的折腾我一下午!!(咬牙切齿.jpg)issue的标签label有长度限制!labels的最大长度限制是50个字符。 1id: '页面 ID', // 可选。默认为 location.href 这个是之前配置的时候提到的机关。id的作用,就是针对一个文章有唯一的标识来判断这篇本章。 在issues里面,可以发现是根据网页标题来新建issues的,然后每个issues有两个labels(标签),一个是gitment,另一个就是id。所以明白了原理后,就是因为id太长,导致初始化失败,现在就是要让id保证在50个字符内。对应配置的id为:1id: '<%= page.title %>' 彩蛋🎊: 如果用网页标题也不能保证在50个字符!最后,我用文章的时间,这样长度是保证在50个字符内,完美解决!(避免了文章每次更新标题或路径时,会重新创建一个issue评论的问题。)1id: '<%= page.date %>' 如果你原来没有设置id这一行,记得在这行后面加逗号,我就栽了傻了。 Gitment的汉化只需到模板里将原来定义CSS和JS的那两行改成: 12<link rel="stylesheet" href="https://billts.site/extra_css/gitment.css"><script src="https://billts.site/js/gitment.js"></script> 所有文章一键初始化评论到本文编写时,还没有一个完善的解决方法,就是用脚本来执行自动化,有需要的可以详细了解:https://github.com/imsun/gitment/issues/5 参考文章:Gitment评论功能接入踩坑教程 文章/网站访问次数统计访问次数统计也有很多方法,这里只简单介绍不蒜子计数方法。很简单,就三步! 安装脚本打开D:\username.github.io\themes\typing\layout_partial\header.ejs在最后加入下面代码: 12<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script> 显示站点总访问量要显示站点总访问量,复制以下代码添加到你需要显示的位置。有两种算法可选:(1)算法a:pv的方式,单个用户连续点击n篇文章,记录n次访问量。 123<span id="busuanzi_container_site_pv"> 本站总访问量<span id="busuanzi_value_site_pv"></span>次</span> (2)算法b:uv的方式,单个用户连续点击n篇文章,只记录1次访客数。123<span id="busuanzi_container_site_uv"> 本站访客数<span id="busuanzi_value_site_uv"></span>人次</span> 打开themes/你的主题/layout/_partial/你可以选择显示在网页的头部header.ejs文件里,文章的article.ejs文件里,或者网页的尾部after-footer.ejs文件里,等等。 显示单页面访问量要显示每篇文章的访问量,复制以下代码添加到你需要显示的位置。与上面同理。算法:pv的方式,单个用户点击1篇文章,本篇文章记录1次阅读量。123<span id="busuanzi_container_page_pv"> 本文总阅读量<span id="busuanzi_value_page_pv"></span>次</span> 代码中文字是可以修改的,只要保留id正确即可。 【提示】:修改标题的文章、隔天再修改内容的文章,git会根据日期做版本控制。每篇文章的访问地址会因此更改。所以为了访问数建议一次性写完不要做改动了。 如果你看到这里,恭喜你,教程已经到此结束啦~快去试试吧! 另外,我是前端零基础小白,我的专业是人工智能机器学习类的,这网页也只是我的随意尝试,这些东西有时候还是会出错,毕竟分享内容才是我的初衷,其他还有很多可以改进的地方。也欢迎大家的意见和指导在下面或者在微博给我留言。我发现一个学习的小窍门就是,在你喜欢的网页右键选择【查看网页源代码】,就能偷学人家的代码啦~嘿嘿😀 有一个我很喜欢的旅游博主,微博:北京小风子,她提到过心理学上有个词叫 positive reinforcement(正加强),这也是我不断写分享的初衷。因为喜欢,才能坚持。希望写的东西对你有帮助,也期待你们的鼓励期待你们的打赏~ 愿大家都玩的开心❤]]></content>
<tags>
<tag>github</tag>
<tag>hexo</tag>
<tag>personal website</tag>
</tags>
</entry>
<entry>
<title><![CDATA[如何搭建自己的个人网站(上)]]></title>
<url>%2F2018%2F04%2F19%2F%E5%A6%82%E4%BD%95%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99%EF%BC%88%E4%B8%8A%EF%BC%89%2F</url>
<content type="text"><![CDATA[本篇的个人网站搭建教程基于 GitHub + Hexo。适宜人群:想要有自己说话的地方,没人干涉。系统环境:win10 搭建正文: 1. 准备软件的安装 Node.js Git 2. 注册github 点击👉https://github.com右上角sign up个人网站的网址是固定格式: username.github.io这个username就是你的github用户名。当然也可以自己买域名啦。 我的GitHub账号:YZHANG1270个人网站:yzhang1270.github.io但是我绑定域名啦~有个更酷炫的网址:codewithzhangyi.com,具体如何绑定将在后续篇介绍。 3. 创建Repository登陆GitHub,点击右上角的 +号,选择New repository 创建一个与你的博客相关的Repository项目进行管理,之后所有你博客的动态都会在这Repository更新。Repository的名字是username.github.io,比如我的yzhang1270.github.io已经创建。其余可以先不填,点击Create repository 4. 配置和使用Github开始—所有应用—找到git bash 5. 配置SSH KeysSSH Keys用来使本地git项目与GitHub联系,这样能在GitHub上的博客项目是最新更新的。 检查SSH Keys的设置首先检查自己电脑上现有的SSH Key: 1$ cd ~/.ssh 如果显示 No such file or directory,说明这是你第一次用git 生成新的SSH Key: 123$ ssh-keygen -t rsa -C "邮件地址@youremail.com"Generating public/private rsa key pair.Enter file in which to save the key (/Users/your_user_directory/.ssh/id_rsa):<回车就好> 这里的邮箱地址,输入注册 Github 的邮箱地址然后系统会要你输入密码:12Enter passphrase (empty for no passphrase):<设置密码>Enter same passphrase again:<再次输入密码> 再回车,这里会提示你输入一个密码,作为你提交项目时使用。这个密码的作用就是在个人网站里所有的改动只能经过你的手,也可以不设置密码,直接为空。注意:输入密码的时候没有输入痕迹的,不要以为什么也没有输入。最后看到这样的界面,就成功设置ssh key了: 添加SSH Key到GitHub上在本地文件夹找到id_rsa.pub文件,看上面的图片第四行的位置告诉你存在哪里了没找到的勾选一下文件扩展名 隐藏的项目.ssh文件夹里记事本打开这个文件复制全部内容到github相应位置回到你的GitHub主页,右上角点击头像选中Setting继续选中左边菜单栏的SSH and GPG keysTitle最好写,随便写。网上有说不写title也有可能后期出现乱七八糟的错误Key部分就是放刚才复制的内容了,点击Add SSH key 6. 测试回到git bash 框里输入以下代码,不要改任何一个字。1$ ssh -T [email protected] 回车,看到如下:123The authenticity of host 'GitHub.com (207.97.227.239)' can't be established.RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.Are you sure you want to continue connecting (yes/no) 输入yes回车1Enter passphrase for key '/c/Users/Yi/.ssh/id_rsa': 输入刚才设置的密码回车,看到“You’ve successfully authenticated…”成功!下一步! 7. 设置用户信息现在已经可以通过 SSH 链接到 GitHub 啦!当然还需要完善一些个人信息:12$ git config --global user.name "yzhang1270" //输入注册时的username$ git config --global user.email "[email protected]" //填写注册邮箱 GitHub 也是用这些信息来做权限的处理,输入下面的代码进行个人信息的设置,把名称和邮箱替换成你自己的。到此,SSH Key配置成功啦!😀本机本机已成功连接到 github。 如有问题,请重新设置。常见错误请参考:Connecting to GitHub with SSHError: Permission denied 9. 搭建Hexo博客利用npm命令安装hexo12$ cd$ npm install -g hexo 创建独立博客项目文件夹安装完成后,关掉之前的Git Bash窗口。在本地创建一个与 Repository中博客项目同名的文件夹username.github.io(如D:/yzhang1270.github.io)在文件夹上点击鼠标右键,选择 Git bash here(搞的我现在每次要写文章的时候脑子里冒出的第一句话永远是Bash Here!) 【提示】在进行博客搭建工作时,每次使用命令都要在D:/yzhang1270.github.io目录下。 执行下面的指令,Hexo 就会自动在 D:/yzhang1270.github.io 文件夹建立独立博客所需要的所有文件啦!1$ hexo init 安装依赖包 1$ npm install 确保git部署 1$ npm install hexo-deployer-git --save 本地查看恭喜你!👏现在已经搭建好本地的 Hexo 博客了,执行完下面的命令就可以到浏览器输入 localhost:4000 查看到啦! 12$ hexo g$ hexo s hexo g 每次进行相应改动都要hexo g 生成一下hexo s 启动服务预览 用Hexo克隆主题执行完 hexo init 命令后会给一个默认的主题:landscape里面还有一篇写好的示例文章:Hello World 你也可以到官网你喜欢的主题进行下载:hexo themes知乎:有哪些好看的 Hexo 主题? 找到之后通过git命令下载界面右侧,在主题的repository点击clone 复制一下那个地址1$ git clone +复制的地址+themes/typing 后面就是clone之后放到你本地的博客文件夹themes文件夹下后面还可以将自己博客个性化装饰~ 修改整站配置文件自己把blog.io中文件都点开看一遍,主要配置文件是 _config.yml,推荐使用 nodepad++ 打开。 修订清单如下,文档内有详细注释,可按注释逐个修订(1)博客名字及作者信息:_config.yml(2)个人介绍页面:about.md 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889这里贴一份网上看到的 可以复制替换原来的 但是替换之前最好备份 可能会出错那要么你就对照着看一下改就好# Hexo Configuration## Docs: http://zespia.tw/hexo/docs/configure.html## Source: https://github.com/tommy351/hexo/# Site 这里的配置,哪项配置反映在哪里,可以参考我的博客title: My Blog #博客名subtitle: to be continued... #副标题description: My blog #给搜索引擎看的,对网站的描述,可以自定义author: Yourname #作者,在博客底部可以看到email: [email protected] #你的联系邮箱language: zh-CN #中文。如果不填则默认英文# URL #这项暂不配置,绑定域名后,欲创建sitemap.xml需要配置该项## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'url: http://yoursite.comroot: /permalink: :year/:month/:day/:title/tag_dir: tagsarchive_dir: archivescategory_dir: categories# Writing 文章布局、写作格式的定义,不修改new_post_name: :title.md # File name of new postsdefault_layout: postauto_spacing: false # Add spaces between asian characters and western characterstitlecase: false # Transform title into titlecasemax_open_file: 100filename_case: 0highlight: enable: true backtick_code_block: true line_number: true tab_replace:# Category & Tagdefault_category: uncategorizedcategory_map:tag_map:# Archives 默认值为2,这里都修改为1,相应页面就只会列出标题,而非全文## 2: Enable pagination## 1: Disable pagination## 0: Fully Disablearchive: 1category: 1tag: 1# Server 不修改## Hexo uses Connect as a server## You can customize the logger format as defined in## http://www.senchalabs.org/connect/logger.htmlport: 4000logger: falselogger_format:# Date / Time format 日期格式,可以修改成自己喜欢的格式## Hexo uses Moment.js to parse and display date## You can customize the date format as defined in## http://momentjs.com/docs/#/displaying/format/date_format: YYYY-M-Dtime_format: H:mm:ss# Pagination 每页显示文章数,可以自定义,贴主设置的是10## Set per_page to 0 to disable paginationper_page: 10pagination_dir: page# Disqus Disqus插件,我们会替换成“多说”,不修改disqus_shortname:# Extensions 这里配置站点所用主题和插件,暂时默认## Plugins: https://github.com/tommy351/hexo/wiki/Plugins## Themes: https://github.com/tommy351/hexo/wiki/Themestheme: landscapeexclude_generator:plugins:- hexo-generator-feed- hexo-generator-sitemap# Deployment 站点部署到github要配置## Docs: http://zespia.tw/hexo/docs/deploy.htmldeploy: type: git repository: branch: master 启用新下载的主题在刚打开的的_config.yml 文件中,找到“# Extensions”,把默认主题 landscape 修改为刚刚下载下来的主题名: 【提示】username.github.io 里有两个 config.yml 文件,一个在根目录,一个在 theme 下,现在修改的是在根目录下的。 更新主题git bash 里执行 12$ cd themes/主题名$ git pull 本地查看调试每次修改都要hexo g 生成一下 12$ hexo g #生成$ hexo s #启动本地服务,进行文章预览调试,退出服务用Ctrl+c 浏览器输入 localhost:4000 预览效果 10. 将博客部署到username.github.io 复制SSH码进入 Github 个人主页中的 Repository,复制新建的独立博客项目username.github.io的 SSH码 编辑整站配置文件打开 D:/username.github.io/_config.yml,把刚刚复制的 SSH码粘贴到repository:后面,别忘了冒号后要空一格。 1234deploy: type: git repository: [email protected]:username/username.github.io.git branch: master 执行下列指令即可完成部署【提示】每次修改本地文件后,需要 hexo g 才能保存。每次使用命令时,都要在你的博客文件夹目录下:在D:/username.github.io/ 右键打开 Git Bash Here 1234# 黄金三命令$ hexo g //(g = generate 修改生产)$ hexo s //(s = server 修改预览)$ hexo d //(d = deploy 修改部署) 【提示】如果在配置 SSH key 时设置了密码,执行 hexo d 命令上传文件时需要输入密码进行确认,会出现一个小框框。输入密码之后在浏览器输入:username.github.io Surprise🎉!恭喜你~你已经拥有一个属于你自己的个人网站啦~嘿嘿 11. 写博客啦!内涵才是重点!在D:\username.github.io\source_posts的空白处右键Git Bash Here1hexo new 'article' 此时已经在D:\username.github.io\source_posts目录下有一个 article.md的Markdown文件Hexo的博客都是用Markdown写的。我就随便写了点试试我的新博客啦~~ 写博文参考:如何写一篇hexo博客 搭建参考: 超详细Hexo+Github博客搭建小白教程 如何搭建一个独立博客——简明Github Pages与Hexo教程 技术小白搭建个人博客 github+hexo 其他参考: 为什么你应该写博客 为什么要自建博客 如果搭建成功了~欢迎打赏哈哈哈~🤑我的文章将持续更新在我的 codewithzhangyi.com ( = yzhang1270.github.io) 里任何疑问请在下方留言,也将在下一期教如何制作留言板~敬请期待~❤]]></content>
<tags>
<tag>github</tag>
<tag>hexo</tag>
<tag>personal website</tag>
</tags>
</entry>
<entry>
<title><![CDATA[基于R的信用评级/评分卡模型制作教程]]></title>
<url>%2F2018%2F04%2F16%2F%E5%9F%BA%E4%BA%8ER%E7%9A%84%E4%BF%A1%E7%94%A8%E8%AF%84%E7%BA%A7-%E8%AF%84%E5%88%86%E5%8D%A1%E6%A8%A1%E5%9E%8B%E5%88%B6%E4%BD%9C%E6%95%99%E7%A8%8B%2F</url>
<content type="text"><![CDATA[注: 本篇是机器学习/数据挖掘在互联网金融行业的应用,只是一个模型建立的流程介绍,不涉及详细的数据清洗逻辑,不涉及模型的调优。 本篇你需要知道的:逻辑回归、WOE、IV值、ROC、KS值。 适用人群:想入门或者想转型成互金风控建模的朋友。 如有任何疑问或建议请在下面留言或者联系我😎 背景:在银行业悠久的历史中,信用评分卡(ScoreCard)模型广泛使用,来判别贷款申请者的逾期概率。现在成为互联网金融行业最火爆的最核心的风控模型。 模型类别:申请卡模型 = A卡(Application Card),场景:贷前行为卡模型 = B卡(Behaviour Card), 场景:贷中催收卡模型 = C卡(Collection Card), 场景:贷后反欺诈模型 = F卡(Anti-Fraud Card), 场景:反欺诈 特征维度:A卡:用户的基本信息 + 自有app操作行为数据 + 第三方数据B卡:用户的基本信息 + 自有app操作行为数据 + 第三方数据 + 历史还款行为数据C卡:用户的基本信息 + 自有app操作行为数据 + 第三方数据 + 历史还款行为数据 + 催款行为数据 本质:(二/多)分类模型 申请卡模型为互金行业最重要、应用最广泛的一张卡,以下介绍以A卡展开。 正文:评分卡(A卡)制作流程传统评分卡使用的算法:逻辑回归(Logistics Regression)传统评分卡构建步骤: 样本收集、数据清洗、时窗切割 分箱、计算WOE和IV值、WOE的性质、变量筛选、循环以上步骤(如需要) 构建逻辑回归模型 评分卡Scaling 评估信用评分卡 选择Cut-Off分数 1. 样本收集、数据清洗、时窗切割 样本收集:导入样本数据 = data0 12345678910111213141516171819202122232425262728293031323334353637383940# 包的下载使用packages<-c("ggplot2","dplyr","smbinning","data.table","woe","gmodels","ROCR","knitr","reshape2","Information","corrgram","corrplot","varhandle","ROCR","stringr","DT","partykit","tcltk","Daim","vcd","caret")UsePackages<-function(p){ if (!is.element(p,installed.packages()[,1])){ install.packages(p)} require(p,character.only = TRUE)}for(p in packages){ UsePackages(p)}library(data.table)library(dplyr)library(ggplot2)library(reshape2)library(corrgram)library(corrplot)library(stats)library(smbinning)library(woe)library(gmodels)library(Information)library(knitr)library(varhandle)library(ROCR)library(stringr)library(DT)library(partykit)library(tcltk)library(Daim)library(vcd)library(caret)options(warn=-1)# 源数据 data0 在data目录下load("data/data0_LR.RData") # 根据历史逾期天数overduedays 增加y变量bad :逾期超过30天为坏客户,否则好客户data0$bad = ifelse(data0$overduedays>30, 1, 0) 数据清洗:本篇为假造数据,只为跑通程序做演示,不适合做数据清洗教程,故此步骤直接用清洗好的数据。 1234567891011121314> names(data0) # 所有特征名 # [1] "extration_amount" "ApplyHour" "AGE_Value" # [4] "CALL_RECORD_FLAG_Value" "CONTACTS_RELATIVES_COUNT_Value" "DEGREE_Value" # [7] "IDENTIFICATION_RESULT_Value" "MARITAL_STATUS_Value" "MONTH_INCOME_Value" # [10] "POSITION_Value" "REJECT_COUNT_Value" "GENDER_Value_ID_CARD" # [13] "WORK_MONTH" "dt_7day" "dt_1month" # [16] "dt_3month" "FINAL_SCORE" "ZM_SCORE" # [19] "state" "bad" "ZM_SCORE_EXIST" # [22] "MISSING_COUNT" > nrow(data0) # 样本数量# [1] 23610> ncol(data0) # 特征数量# [1] 22 时窗切割:信用评等最主要的功能为预测客户未来的违约行为,因此必须针对预测时间点进行明确的定义。这个就是时窗切割(Time Windows)。时窗的时间根据每个产品的业务逻辑确定。在此我回溯过去半年数据来预测未来新用户的逾期概率。抽样时窗(Sample Windows):进行预测时,必须回溯多久以前的客户历史行为数据。观察时窗(Performance Windows):进行预测时,要预估未来多久客户的行为结果。 2. 分箱、计算WOE和IV值、WOE的性质、变量筛选、循环以上步骤(如需要) 分箱(Binning):对连续变量离散化(Discretization),对离散变量也可进行重新分箱、组合。分箱方式:等宽分箱、等频分箱、最优分箱等。本文使用最优分箱,基于最小熵原则。 WOE(Weight of Evidence)和IV(Infomation Value):逻辑回归是线性的统计模式,因此遇到非线性趋势的变数会造成无法有效的建立预测模型,因此需要WOE。计算逻辑点击这里 WOE = ln(Odds) = ln(%Good/%Bad) = ln(p/(1-p))IV= ∑(%Good-%Bad)*WOE = ∑(%Good-%Bad)*ln(%Good/%Bad) 🌝WOE的性质(划重点!): (1) WOE与风险正相关,WOE越大,风险越高,代表该层级的客户品质越差。如果WOE接近0,表示接近平均水平。(正负相关可以调节)(2)进行WOE检定时,观察WOE分布的变动趋势是否符合逻辑(Logical Trend).所谓Logical Trend指的是WOE变动趋势必须呈现递增、递减,或者是单纯转折模式(u型或n型)。(3)如果WOE趋势呈现不稳定的锯齿状波动(W型或M型)或者是不同时窗呈现不一致的趋势,此时就必须通过重新分箱来调整,否则就必须放弃此变量。(4)WOE不会因为抽样误差造成数值大幅变化。而且WOE制作的评分卡可解释性强,也是这套评分卡永流传的精髓之一。 变量筛选:根据每个变量的分箱结果计算IV值,留下IV>0.1的变量。这个0.1的数值可以改变。123456# 计算dataframe里所有特征的IV值IV <- create_infotables(data=data0, y="bad",bins = 10, ncore = NULL, parallel=FALSE)# 显示IV计算结果(Summary<-IV$Summary) 绘制每个变量的WOE分箱柱状图12345678910111213141516171819202122232425# 筛选变量:留下IV>0.1的变量Summary=Summary%>% filter(Summary$IV>0.1)%>% as.data.frame()(selected_names<-Summary$Variable) # 显示筛选后的变量名# [1] "ZM_SCORE" "IDENTIFICATION_RESULT_Value" # [3] "extration_amount" "CONTACTS_RELATIVES_COUNT_Value"# [5] "POSITION_Value" "ZM_SCORE_EXIST" num<-length(selected_names) # 筛选后的变量个数# 绘制每个变量的WOE分箱柱状图names <- selected_names # LOOP for ALL: names<-names(IV$Tables)plots <- list()IVtable<- IV$Tablesfor (i in 1:length(selected_names)){ plots[[i]] <- plot_infotables(IV, names[i],same_scales=FALSE,show_values = TRUE) IVtable[i]<-IV$Tables$names[i]}# Showing the variables whose iv >0.1plots[1:length(selected_names)]# MultiPlot(IV, IV$Summary$Variable[1:num]) # 绘制综合图codeIVtable[selected_names] 根据上文提到的(Logical Trend)来观察上面6个WOE分布图,ZM_SCORE, IDENTIFICATION_RESULT_Value, CONTACTS_RELATIVES_COUNT_Value, POSITION_Value, ZM_SCORE_EXIST都符合Logical Trend。只有extration_amount的WOE分布呈现波浪不规则型,需要整改。 相关性分析(CORRplot):这里只先示范协方差矩阵图1234567891011121314151617col1 <- colorRampPalette(c("#7F0000","red","#FF7F00","yellow","white", "cyan", "#007FFF", "blue","#00007F"))col2 <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", "#FDDBC7", "#FFFFFF", "#D1E5F0", "#92C5DE", "#4393C3", "#2166AC", "#053061"))col3 <- colorRampPalette(c("red", "white", "blue"))col4 <- colorRampPalette(c("#7F0000","red","#FF7F00","yellow","#7FFF7F", "cyan", "#007FFF", "blue","#00007F"))wb <- c("white","black")par(ask = TRUE)data0= data0%>% select(selected_names,bad)%>% as.data.frame()M=data0[complete.cases(data0),]M<-cor(M)corrplot(M, method="color", col=col1(20), cl.length=21,order = "AOE",tl.cex = 0.6,addCoef.col="grey") 删去CONTACTS_RELATIVES_COUNT_Value 循环分箱步骤(分箱调整)(1)extration_amount1234567891011data0$extration_amount=as.numeric(data0$extration_amount)data_tmp=data0%>% select(c(extration_amount,bad))%>% apply(2,as.numeric)%>% data.frame()IV <- create_infotables(data_tmp, y='bad', ncore=2,bins=5) # bins的数值随意定,一般2~10data0$extration_amount=cut(data0$extration_amount,breaks=c(-Inf,475,671,771,971,Inf),labels = IV$Tables$v$WOE[1:length(IV$Tables$extration_amount$WOE)])ggplot(IV$Tables$extration_amount,aes(x=extration_amount,y=WOE))+ geom_bar(stat='identity',fill='lightblue') 12345# WOE计算结果保留,在步骤4-Scaling会再次用到IV$Tables$extration_amount$WOE# [1] -0.4414730 -0.2142640 0.4596698 0.4386113 -0.3501923IV$Summary# 0.1327355 #IV值 (2)POSITION_Value12345678910data0$POSITION_Value=as.numeric(data0$POSITION_Value)data_tmp=data0%>% select(c(POSITION_Value,bad))%>% apply(2,as.numeric)%>% data.frame()IV <- create_infotables(data_tmp, y='bad', ncore=2,bins=6)ggplot(IV$Tables$POSITION_Value,aes(x=POSITION_Value,y=WOE))+ geom_bar(stat='identity',fill='lightblue')data0$POSITION_Value=cut(data0$POSITION_Value,breaks=c(-Inf,0,1,5),labels = IV$Tables$POSITION_Value$WOE[1:length(IV$Tables$POSITION_Value$WOE)]) 12345# WOE计算结果保留,在步骤4-Scaling会再次用到IV$Tables$POSITION_Value$WOE# [1] 0.5817640 -0.2522849 -0.2905931IV$Summary# 0.1528563 (3)ZM_SCORE1234567891011data0$ZM_SCORE=as.numeric(data0$ZM_SCORE)data_tmp=data0%>% select(c(ZM_SCORE,bad))%>% apply(2,as.numeric)%>% data.frame()IV <- create_infotables(data_tmp, y='bad', ncore=2,bins=10)ggplot(IV$Tables$ZM_SCORE,aes(x=ZM_SCORE,y=WOE))+ geom_bar(stat='identity',fill='lightblue')data0$ZM_SCORE=cut(data0$ZM_SCORE,breaks=c(-Inf,549,569,592,609,635,Inf),labels = IV$Tables$ZM_SCORE$WOE[1:length(IV$Tables$ZM_SCORE$WOE)]) 123456# WOE计算结果保留,在步骤4-Scaling会再次用到IV$Tables$ZM_SCORE$WOE# [1] 0.40926664 0.30817452 -0.01635135# [4] -0.38743811 -0.74663108 -1.52210534IV$Summary# 0.2749328 (4)ZM_SCORE_EXIST1234567891011data0$ZM_SCORE_EXIST=as.numeric(data0$ZM_SCORE_EXIST)data_tmp=data0%>% select(c(ZM_SCORE_EXIST,bad))%>% apply(2,as.numeric)%>% data.frame()IV <- create_infotables(data_tmp, y='bad', ncore=2,bins=2)ggplot(IV$Tables$ZM_SCORE_EXIST,aes(x=ZM_SCORE_EXIST,y=WOE))+ geom_bar(stat='identity',fill='lightblue')data0$ZM_SCORE_EXIST=cut(data0$ZM_SCORE_EXIST,breaks=c(-Inf,0,1),labels = IV$Tables$ZM_SCORE_EXIST$WOE[1:length(IV$Tables$ZM_SCORE_EXIST$WOE)]) 12345# WOE计算结果保留,在步骤4-Scaling会再次用到IV$Tables$ZM_SCORE_EXIST$WOE# [1] 0.2976555 -0.3847163IV$Summary# 0.1134327 (5)IDENTIFICATION_RESULT_Value1234567891011data0$IDENTIFICATION_RESULT_Value=as.numeric(data0$IDENTIFICATION_RESULT_Value)data_tmp=data0%>% select(c(IDENTIFICATION_RESULT_Value,bad))%>% apply(2,as.numeric)%>% data.frame()IV <- create_infotables(data_tmp, y='bad', ncore=2,bins=5)ggplot(IV$Tables$IDENTIFICATION_RESULT_Value,aes(x=IDENTIFICATION_RESULT_Value,y=WOE))+ geom_bar(stat='identity',fill='lightblue')data0$IDENTIFICATION_RESULT_Value=cut(data0$IDENTIFICATION_RESULT_Value,breaks=c(-Inf,2,3,4),labels = IV$Tables$IDENTIFICATION_RESULT_Value$WOE[1:length(IV$Tables$IDENTIFICATION_RESULT_Value$WOE)]) 12345# WOE计算结果保留,在步骤4-Scaling会再次用到IV$Tables$IDENTIFICATION_RESULT_Value$WOE# [1] 0.6084594 -0.2568459 -0.4399638IV$Summary# 0.1897237 以上的5个变量的IV>0.1,且WOE分布呈Logical Trend,保存数据1data1 = data0 #备份数据,以下都对data1进行处理 3. 构建逻辑回归模型1234567891011121314151617data1[, c(1:length(data1))] <- sapply(data1[, c(1:length(data1))], as.numeric)cbind(apply(data1,2,function(x)length(unique(x))),sapply(data1,class))#拆分训练集与测试集,建模#----------------------------------------------------------# train & test(80%-20%) select randomly#----------------------------------------------------------nrow(data1)a = round(nrow(data1)*0.8)b = sample(nrow(data1), a, replace = FALSE, prob = NULL)data_train= data1[b,]data_test = data1[-b,]# 逻辑回归建模m1=glm(bad~., data=data_train,binomial(link='logit'))summary(m1) 1234通过检验截距为0.72215各个系数为-0.39815, -0.42831, 0.08642, -0.24813, 0.25519这些参数都十分重要,在Scaling中再次用到 123456anova(m1,test="Chisq") # ANOVA 检验通过model=m1# y值预测yhat_train = fitted(model)yhat_test = predict(model,newdata=data_test,type='response') 4. 评分卡Scaling 将WOE值转换为信用风险分数12345678data2 = data1 #数据备份odds=sum(data2$bad==1)/sum(data2$bad==0)log(odds,base=exp(1))B=40/log(2,base=exp(1))A=200-B*log(odds,base=exp(1))score=yhat_test*B+A #Score=258.152490+57.707802*yhat 1234summary(score)# Min. 1st Qu. Median Mean 3rd Qu. Max. # 261 268 273 274 278 295 #这批客户的信用风险分值已经得出 好/坏客户分数分布分数越高,客户的逾期风险越高,因此坏客户应该集中在分数偏高区域,反之,好客户应该集中在风险分数低分区域。123456index=which(data_test$bad==1)m=seq(260,300,by=5) #260,300是根据分数值域定的,5为间隔数值bad=cut(score[index],m)%>%table%>%data.framecolnames(bad)=c('level','count')ggplot(data = bad,aes(x =level,y=count)) + geom_bar(stat = 'identity') 1234index=which(data_test$bad==0)good=cut(score[index],m)%>%table%>%data.framecolnames(good)=c('level','count')ggplot(data = good,aes(x =level,y=count)) + geom_bar(stat = 'identity') 5. 评估信用评分卡 KS检验:模型区分好坏客户的力度KS>0.3时,模型才能用。 ROC检验:模型判别真假的准确度AUC>0.7时,模型才能用。此模型因为数据质量和分箱质量,所以不具有参考性,仅做跑通模型之用,具体的模型调优将另外写。 我的天真的是不知不觉写那么长,其实还有很多还没说,后续会慢慢写后续的。那么恭喜你,到此,你的评分卡已经做完啦~~每个客户只要填写根据你筛选出来的变量的相关信息,就能得到每个人专属的信用风险分啦!🖖 6. 选择Cut-Off分数模型做完,下一步就是要跟业务结合啦~模型的作用就是评估贷款申请者的未来逾期概率。风险高的拒掉,风险低的通过申请,那么如何划定这个决策的分数界限呢?多少分数应该通过,多少分数应该拒绝? 画出每阶层的KS值,最高值对应阶层为决策阈值根据上图可以看出是第三阶层的KS=0.2637最大。按照之前分层的结果,这层级的客户的分数区间是(270,275](1)270分以下客户:通过(2)270-275分:人工审核(3)275分以上客户:拒绝 但是,KS只是做决策的某方面根据而已,也可根据每阶层的违约率决定决策阈值,同时也要观察每阶层的人数分布。最极端的例子就是,一下子拒绝掉所有申请者,这样逾期率就是0了,但是也就关门大吉啦~感谢看到这里的朋友,A卡制作就在此告一段落啦😊 课后题😉 计算下面每个变量的WOE值对应的信用风险分数: 1234567891011121314151617181920# 伪代码...extration_amount[WOE] -0.4414730 -0.2142640 0.4596698 0.4386113 -0.3501923[系数b] 0.08642POSITION_Value[WOE] 0.5817640 -0.2522849 -0.2905931[系数b] -0.24813ZM_SCORE[WOE] 0.40926664 0.30817452 -0.01635135 -0.38743811 -0.74663108 -1.52210534[系数b] -0.39815ZM_SCORE_EXIST[WOE] 0.2976555 -0.3847163[系数b] 0.25519IDENTIFICATION_RESULT_Value$WOE[WOE] 0.6084594 -0.2568459 -0.4399638[系数b] -0.42831 思考分数换算的Scaling中,A与B的作用 提示:在第四大步骤Scaling中,有计算逻辑 福利❤源数据下载👍 打赏者在备注留下邮箱都会赠予相关材料之后会增加基于大杀器XGBOOST制作的评分卡,而且有R有Python!敬请期待~]]></content>
<tags>
<tag>machine learning</tag>
<tag>R</tag>
<tag>model</tag>
<tag>scorecard</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2017校招/社招总结(人工智能岗)]]></title>
<url>%2F2018%2F04%2F03%2F2017%E6%A0%A1%E6%8B%9B%E7%A4%BE%E6%8B%9B%E6%80%BB%E7%BB%93%EF%BC%88%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E5%B2%97%EF%BC%89%2F</url>
<content type="text"><![CDATA[这个岗位的叫法很多:人工智能/数据挖掘/机器学习/…核心是偏数学算法的,跟传统写C/Java类程序员不太一样。 【本人背景】 可作为2016/17/18应届生 2012.9-2016.7 degree of bachelor 2016.9-2017.11 degree of master (Hong Kong) 如何准备校招【整体节奏的把控】大厂的开放时间会比较早,密切关注网申时间节点: 2016届的秋招:2016年7月 - 2016年11月 2017届的春招:2017年2月 - 2017年4月 2017届的暑期实习:2017年3月 - 2017年5月 2017届的秋招:2017年7月 - 2017年11月 2018届的春招:2018年2月 - 2018年4月 2018届的秋招:2018年7月 - 2018年11月 【手撕代码能力】这个提早准备没错的。以前被问敲过几行python觉得这个问题很蠢,但实际就是敲的越多你踩过的坑就越多,你能快速独立解决的问题就越多。 主流语言:R、Python 多数在大厂的面试:相关包的使用,及具体场景问题,比如内存不够的解决方案等。 “研发”和“大数据”跟机器学习有本质的区别,但在实际工作应用中有许多穿插工作,避免不了的被提问“数据结构及算法类(C/Java)”和“大数据类(Hadoop)”的问题。当然不强求,知道更多就是加分项。 建议提早半年开始准备。我的代码也是从实习开始敲起,敲了半年才觉得下手如有神哈哈。不要做没实际意义的课后题,也不要照着书本例题敲,敲完你就忘了,书本这些都是已经排除万难的东西,得不到什么成长。 入门修炼:全国大学生数学建模竞赛、全美大学生数学建模竞赛、kaggle、天池… 【项目经历/实习经历】如果明确自己的职业方向为人工智能/数据挖掘类的,请不要浪费时间去申请其他与技术无关的实习。端茶送水,外卖跑腿,打印纸并不能帮你。当时由于身边同学都断断续续出去实习,面前有一份大厂行政的实习,我…竟然犹豫了一下,好在也还是拒绝了。 尽量选择大厂的技术实习,毕竟以后想进去会更难。但是不要因为一个月拿3000块就只干3000块的活。把整个项目跟下来,了解框架的架构,优化的方向,多去尝试,就算加班(加班在深圳很正常)也是你赚到,思考如何简化重复性工作,去尝试了解自己部门和其他部门的工作内容与方向,了解的越多你对自己想做的事情了解的也越多。我实习做的评分卡模型,就是二分类,除了传统逻辑回归,也尝试新的XGB等等,而且虽然别人也在做,但是私下自己会把整个模型写一遍,包含数据清洗和模型调优等,这样对业务的了解也更透彻,面试起来所有的细节都是亲手做过的,也就比较顺了。 如果没有实习在手,世界给我们数据挖掘选手的大门还是敞开着的。大学生有全国的数学建模大赛,再高逼格的还有kaggle,真刀真枪的题下不了手,kaggle上还有专门给数据挖掘入门者的练习场。相关的比赛还有很多,包括腾讯、阿里等大厂也时不时会发布算法大赛,目测这样的算法大赛只会越来越多,你坚持做完一个项目,你在平台上还可以得到相关名次,名次越靠前越有利哈哈哈这是废话。 【临时佛脚该抱还得抱】 数据库:sql、hive语句的基本用法W3school 基本算法的推导:SVM/BP/LR/CART/GBDT/XGB/KNN/KMEANS/… 基本原理的逻辑:决策树的剪枝等/神经网络的dropout等/损失函数/正则化/… 基本的包的使用:python的pandas/numpy/sklearn; R的dplyr/data.table/… 必备问题:为什么想来这家公司?/为什么离开上家公司?(提前了解面试公司背景和产品) 完整面筋+总结 校招== 微众银行 ==网申 / 在线笔试(4大题) 有近一年各类数据,除去节假日带来的销售增长,预测某家商场在国庆节假日原本的销售量。(用时间序列模型角度解题) 一个市中心的宠物店,为何周末的顾客访问量低于上班日?(考虑养宠物的人群在工作日与周末的习惯) 中位数与平均数的关系(箱图解释大于等于小于三种样本类型) 如何判别某高校食堂的点餐者是在校学生还是社会人士?(周期性的买餐时点与频率等) 感想就是,自己判别是聚类还是分类等,自己决定哪些是需要的特征,哪个是y值,四大题都是思路题,没有编程大题。 == 顺丰科技 ==网申 / 在线笔试(10个选择题 + 1道思路题) 选择题范围:损失函数/正则化/基本算法/栈/… 大题:SVM的应用:检测快递包裹内违禁物品(决定x值与y值) == 美图秀秀 ==网申 / 在线笔试(10个选择题 + 1道编程题)整场笔试题比较偏向研发类,考到较多的Java和C相关,而基本没有R与Python。所以不推荐擅长数学专业的学生尝试,推荐计算机专业的爱滤镜爱美颜的同学去试试哈~ 社招(全部为内推机会)== 微保 ==车保反欺诈 / 一面(现场) 现在在微信的腾讯服务模块已经上线了,第一次这么靠近腾讯大厦呀内心这个激动的,本人当时对腾讯有种盲目的崇拜。 自我介绍 项目介绍 项目LR和XGB原理、区别 类别不均衡如何处理 反欺诈场景:如何判别骗保行为(假设你可以拥有所有你想要的数据,当时觉得真不愧腾讯爸爸) 为什么XGB比GBDT好 数据清洗有哪些 PCA的原理,计算推导 变量筛选有哪些方法 信息增益的计算公式 样本量很少情况下如何建模 交叉检验的实现 决策树如何剪枝 WOE/IV值计算公式 分箱有哪些方法 最优分箱的原理 == 鹅厂 ==视频推荐数据挖掘工程师 / 一面(现场) 自我介绍 手推SVM:目标函数,计算逻辑,公式都写出来,平面与非平面 项目介绍 XGB原理介绍,参数介绍,决策树原理介绍 数据清洗步骤 Linux熟悉程度 C/Java熟悉程度 过拟合如何解决 除了LR/XGB还实现过什么算法 分箱有哪些 核函数有哪些 腾讯游戏:用户画像大数据分析工程师 / 一面(电话面) 自我介绍 项目介绍 LR的原理,目标函数 XGB比GBDT好在哪里 熟悉腾讯哪款游戏?(王者荣耀) 游戏反欺诈场景题:在王者荣耀中,假设你拥有所有你所需数据,如何判别玩家挂机? 游戏反欺诈场景题:在王者荣耀的挂机玩家中,如何判别对方是主动挂机(坑队友)还是被动挂机(进入地铁信号不好等)? 平时通过什么渠道学习机器学习?(好问题值得好好准备) 开发需求:LINUX环境的熟悉程度 Java/C++的基本操作与后台维护 python如何实现数值传递 == 顺丰科技 ==大数据挖掘与分析工程师 / 一面(现场) 运筹学最熟悉哪个算法 场景题:双十一快递爆发时,如何减少快递员的劳动力?(仓库选址问题) XGB与GBDT的区别 XGB有哪些参数,取指范围,各代表什么意思 排列组合概率题 如何通过线性规划实现最优送货路径 决策树先剪枝还是后剪枝好 损失函数有哪些 最近机器学习比较火的书籍 除了R与Python外,MATLAB熟悉程度 偏向做数据挖掘还是算法研究 说一个聚类算法 C++的语法知道哪些 类别不均衡的处理方式有哪些 bagging与boosting的区别 数据标准化有哪些方法 模型评估指标有哪些 解释模型复杂度 onehot原理 正则化如何实现 说出一个聚类算法(KMEANS) 机器学习与人工智能工程师 / 一面(现场) 自我介绍 项目介绍-评分卡模型 评分卡模型的原理 = LR原理 或 二分类原理 评分卡模型的最终结果如何转换成决策规则,KS值起到什么作用 为什么要转行/为什么来顺丰 数据清洗方法有哪些 WOE的作用 缺失值填充方式有哪些 变量筛选方法有哪些 ROC计算逻辑 机器学习与人工智能工程师 / 二面BOSS面(现场) 统计检验有哪些 t检验如何使用 场景题:两个卖场差价只有1分钱,你从哪个卖场进货?(超级简单的问题,没有限制条件,当然从便宜的那家) 项目-评分卡制作介绍 什么时候能上班 当时面过了技术面和BOSS面,卡在HR面说是因为我没有工作经验就把我拒了,内心真的讨厌顺丰HR,所以对顺丰没什么好感,可能当时真的寄予厚望的,太让人失望了 == 魅族科技 ==人工智能与数据挖掘工程师 / 一面(技术+BOSS)(现场) 自我介绍 项目介绍 决策树的优点 如何判断一个模型中的变量太多 决策树与其他模型的损失函数、复杂度的比较 python遇到大数据量时如何处理空间问题 决策树能否有非数值型变量 决策树与神经网络的区别与优缺点对比 过拟合如何处理 类别不均衡如何处理 数据结构有哪些 XGB的参数有哪些 model ensembling的方法有哪些 模型复杂度与什么有关 线性回归与逻辑回归的区别 大数据了解多少?Hadoop使用程度 == 绿米(offer) ==人工智能工程师 / 一面(英文现场) 很可爱的老外技术官,直接把我的简历翻过去,空白面朝上。并说他的面试风格就是与对面的人沟通而不注重纸上的字。然后就以问我的名字读音和我的家乡开始了我终身难忘的面试~~ 你在香港读书,听说香港人都很排外是这样的吗? 假如咱们的桌子上现在有个阿拉丁神灯,你会许哪三个愿望? 你最讨厌的事情? 面试官Tony讲了很多他的故事并且给我推荐了几本书 展示了绿米的产品,也激动的告诉HR ‘You must hire her!’ 别看好像这个面试很无聊,重点在三个愿望那里,我在我最焦虑的时候日日夜夜思考我到底要什么,到底来这世间为了什么,做什么事情能让我从心底里开心而不是将自己浮于众人的唾液之下。很庆幸这场面试没有技术面,但是我连续三天熬夜实打实的准备了,手推了所有我知道的算法,说实话我也不怕就算问到什么我不知道至少我尽全力了。但是好像就是那个能看懂我的人终于出现,他愿意以公司给我提供平台,让我实现自己的想法与愿望,真的别无所求。所以,无论那些笔试是否有回音,我也不在乎了,就是绿米了。我的求职之路就在此告一段落了。 总结: 每个面试的结尾,面试官会问你有没有什么想问的,请注意这个问题也很关键:比如:这个小组目前在做什么项目/实现项目主要用什么语言和算法/…尽量不要问加不加班,有没有加班费之类的,别问我为什么这么说…在面试中遇到不理解的,比如C++语法不懂,可以问这个C++具体在项目中实现什么功能如果你提出好问题,能再次引起面试官对你的兴趣,那就能增加面试成功率 应届生就好好准备校招,别懒,别怕输,别怕被拒,从哪里跌倒从哪里起来社招不是你能招呼的,会更挫败,因为你什么也没做过 虽然是做技术的,但是日常social一下还是收益很大的实习的时候,也要与周围同事和平相处,尤其是老大哥们,也许哪天他就帮你内推大厂去了内推你能知道意想不到的信息,面试官,岗位需求,最近在做什么项目之类的 挑选给你机会的公司,不要浪费自己的时间不要每家都去,去之前了解这家公司与你的匹配度尤其社招,你一改动简历就很多人给你打电话,你要有策略的去进行面试,把握总结每个机会像我就是东一榔头西一榔头的,好多都是止步于第一面,就没回信儿了,因为每次面完没有好好反思总结,等下次再遇到这问题还是抓瞎,十分消耗自己的时间和信心 在此鞠躬感谢所有帮我内推小哥哥小姐姐,铭记于心,来日必报答。 写在最后:永远不要放弃自己!每次面试前做充足的准备,默念:“放轻松放轻松放轻松”;面试后进行反思总结,默念:“看缘分看缘分看缘分” ~]]></content>
<tags>
<tag>interview</tag>
<tag>job</tag>
<tag>machine learning</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Kaggle日记 - 基于R的Titanic可视化分析]]></title>
<url>%2F2018%2F03%2F26%2Fkaggle%E6%8C%91%E6%88%98%E6%97%A5%E8%AE%B0--Titanic%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E6%9E%90%2F</url>
<content type="text"><![CDATA[Titanic为Kaggle入门赛之一,类别为二分类的监督模型。样本数据可自行前往官网下载,csv格式(train + test) 以下为我用R对源字段数据的分析: get data12345df_train = read.csv("data/train.csv")%>% as.data.table() df_test = read.csv("data/test.csv")%>% as.data.table() dataAll = rbind(df_train, df_test, fill = T) 多图绘制1234567891011121314151617181920212223242526272829303132333435multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) { library(grid) # Make a list from the ... arguments and plotlist plots <- c(list(...), plotlist) numPlots = length(plots) # If layout is NULL, then use 'cols' to determine layout if (is.null(layout)) { # Make the panel # ncol: Number of columns of plots # nrow: Number of rows needed, calculated from # of cols layout <- matrix(seq(1, cols * ceiling(numPlots/cols)), ncol = cols, nrow = ceiling(numPlots/cols)) } if (numPlots==1) { print(plots[[1]]) } else { # Set up the page grid.newpage() pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout)))) # Make each plot, in the correct location for (i in 1:numPlots) { # Get the i,j matrix positions of the regions that contain this subplot matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE)) print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row, layout.pos.col = matchidx$col)) } }} sex123456789101112131415sex_analysis = function(trainDf){ a<-CrossTable(trainDf$Sex, trainDf$Survived) par(mfrow=c(1,2)) barplot(a$t[,2], beside = TRUE, sub = "survival count of both sexes", ylab = "survival count", xlab = "Sex", ylim = c(0,250),col = c("mistyrose","lightblue")) # 250 shall change barplot(a$prop.row[,2], beside = TRUE, sub = "survival rate of both sexes", ylab = "survival rate", xlab = "Sex", ylim = c(0,0.80),col = c("mistyrose","lightblue")) # 0.8 shall change}sex_analysis(df_train) age1234567891011121314151617age_analysis = function(trainDf){ ageDf = trainDf[!is.na(trainDf$Age),] ageDf$Age = as.integer(ageDf$Age) survivedDf = ageDf[ageDf$Survived == 1] m=seq(0,max(ageDf$Age),by=5) survivedAge=cut(survivedDf$Age,m)%>%table%>%data.frame ageDfAge=cut(ageDf$Age,m)%>%table%>%data.frame survivedAge = data.frame(survivedAge, round(survivedAge$Freq/ageDfAge$Freq, digits = 4)) colnames(survivedAge)=c('Age','count', 'prop') # aveRate = sum(survivedAge$prop[1:13])/13 p1 <- ggplot(data = survivedAge,aes(x =Age,y=count)) + geom_bar(stat = 'identity') + ggtitle("age distribution of survivors") p2 <- ggplot(data = survivedAge,aes(x =Age,y=prop)) + geom_bar(stat = 'identity') + ggtitle("age freq distribution of survivors") multiplot(p1, p2)}age_analysis(df_train) fare12345678910111213141516171819fare_analysis = function(trainDf){ trainDf$Fare = as.integer(trainDf$Fare) survivedDf = trainDf[trainDf$Survived == 1] notSurvivedDf = trainDf[trainDf$Survived == 0] survivedFare = survivedDf$Fare%>%table%>%data.frame colnames(survivedFare) = c('Fare', 'Count') notSurvivedFare = notSurvivedDf$Fare%>%table%>%data.frame colnames(notSurvivedFare) = c('Fare', 'Count') p1 <- ggplot(data = survivedFare,aes(x =Fare,y=Count)) + geom_bar(stat = 'identity') + ggtitle("fare distribution of survivors") p2 <- ggplot(data = notSurvivedFare,aes(x =Fare,y=Count)) + geom_bar(stat = 'identity') + ggtitle("fare distribution of NOT survivors") p3 <- barplot(height = cbind('survived'= mean(survivedDf$Fare), 'notSurvived' = mean(notSurvivedDf$Fare)), main = 'average fare', ylab = 'fare', ylim = c(0,50),col = "lightblue") # 50 shall change multiplot(p1, p2)}fare_analysis(df_train) pclass123456789101112131415pclass_analysis = function(trainDf){ a <- CrossTable(trainDf$Pclass, trainDf$Survived) par(mfrow=c(1,2)) barplot(a$t[,2], beside = TRUE, sub = "survival count of pclass", ylab = "survival count", xlab = "pclass", ylim = c(0,140),col = c("mistyrose","lightblue","lightyellow")) # 250 shall change barplot(a$prop.row[,2], beside = TRUE, sub = "survival rate of pclass", ylab = "survival rate", xlab = "pclass", ylim = c(0,0.70),col = c("mistyrose","lightblue","lightyellow")) # 0.8 shall change}pclass_analysis(df_train) sibsp12345678910111213141516171819202122232425sibsp_analysis = function(trainDf){ trainDf = data.frame(trainDf, sibsp = ifelse(trainDf$SibSp>0, 1, 0)) a <- CrossTable(trainDf$SibSp, trainDf$Survived) b <- CrossTable(trainDf$sibsp, trainDf$Survived) par(mfrow=c(2,2)) barplot(a$t[,2], beside = TRUE, sub = "survival count of sibsp", ylab = "survival count", xlab = "sibsp", ylim = c(0,140),col = c("mistyrose","lightblue","lightyellow")) # 250 shall change barplot(a$prop.row[,2], beside = TRUE, sub = "survival rate of sibsp", ylab = "survival rate", xlab = "sibsp", ylim = c(0,0.70),col = c("mistyrose","lightblue","lightyellow")) # 0.8 shall change barplot(b$t[,2], beside = TRUE, sub = "survival count of sibsp", ylab = "survival count", xlab = "sibsp", ylim = c(0,250),col = c("mistyrose","lightblue","lightyellow")) # 250 shall change barplot(b$prop.row[,2], beside = TRUE, sub = "survival rate of sibsp", ylab = "survival rate", xlab = "sibsp", ylim = c(0,0.50),col = c("mistyrose","lightblue","lightyellow")) # 0.8 shall change}sibsp_analysis(df_train) Parch12345678910111213141516171819202122232425Parch_analysis = function(trainDf){ trainDf = data.frame(trainDf, parch = ifelse(trainDf$Parch>0, 1, 0)) a <- CrossTable(trainDf$Parch, trainDf$Survived) b <- CrossTable(trainDf$parch, trainDf$Survived) par(mfrow=c(2,2)) barplot(a$t[,2], beside = TRUE, sub = "survival count of Parch", ylab = "survival count", xlab = "Parch", ylim = c(0,140),col = c("mistyrose","lightblue","lightyellow")) # 250 shall change barplot(a$prop.row[,2], beside = TRUE, sub = "survival rate of Parch", ylab = "survival rate", xlab = "Parch", ylim = c(0,0.70),col = c("mistyrose","lightblue","lightyellow")) # 0.8 shall change barplot(b$t[,2], beside = TRUE, sub = "survival count of Parch", ylab = "survival count", xlab = "Parch", ylim = c(0,250),col = c("mistyrose","lightblue","lightyellow")) # 250 shall change barplot(b$prop.row[,2], beside = TRUE, sub = "survival rate of Parch", ylab = "survival rate", xlab = "Parch", ylim = c(0,0.50),col = c("mistyrose","lightblue","lightyellow")) # 0.8 shall change}Parch_analysis(df_train) corr123456789101112131415161718col1 <- colorRampPalette(c("#7F0000","red","#FF7F00","yellow","white", "cyan", "#007FFF", "blue","#00007F"))col2 <- colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", "#FDDBC7", "#FFFFFF", "#D1E5F0", "#92C5DE", "#4393C3", "#2166AC", "#053061"))col3 <- colorRampPalette(c("red", "white", "blue"))col4 <- colorRampPalette(c("#7F0000","red","#FF7F00","yellow","#7FFF7F", "cyan", "#007FFF", "blue","#00007F"))wb <- c("white","black")par(ask = TRUE)#-----------------------# correlation analysis# cov = covariance matrix# cor = correlation matrixM<-cor(train)corrplot(M, method="color", col=col1(20), cl.length=21,order = "AOE",tl.cex = 0.6,,addCoef.col="grey")]]></content>
<tags>
<tag>kaggle</tag>
<tag>R</tag>
<tag>visualization</tag>
<tag>可视化分析</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018 List]]></title>
<url>%2F2018%2F01%2F20%2F2018-List%2F</url>
<content type="text"><![CDATA[其实是真的非常感慨2017年,比起积累,可能更大的进步是知道了方向。那么2018年的想法就是知行合一吧。 Rough List Jan 2018年的职业计划,买新电脑,搭建各种环境 Feb kaggle比赛开启 Mar 找开源的项目做,github上 Apr 爬虫—python May 总结过去一年实现过的算法,写教程 Jun C语言算法—-数据结构与算法 July 丰富github/repository Aug 做一个小游戏—GitHub上的开源 Sep 吉他/钢琴/架子鼓 Oct 大数据—mapreduce/ spark/ Nov 潜水/降落伞/深海 Dec 如何平衡:学习 计算机语言 算法/工作 开源项目/健身/阅读 书单/出行———所有的频率控制 这里,只记录一个热爱敲代码的美少女的中二生活。]]></content>
<tags>
<tag>plan</tag>
<tag>2018</tag>
</tags>
</entry>
</search>