1 RNN
上节课我们利用词嵌入
把句子转化为词向量序列
的详细过程,但忽略了语言数据在时间上的关联性
,这节课来让我们的神经网络具有处理这种关联的能力
我们不考虑预处理这一过程,假设都已经处理成了合适的300维词向量,现在我们开始改造一下神经网络的工作模式:
我们将每次的输出值保存起来,与下一个词向量一起作为下一次的输入,直到得到最后的预测输出。
这样一个句子的每个词对最后预测输出的影响就在每一次的保存和下一步的数据的共同作用
中持续到了最后,我们把这样的神经网络称为:RNN 循环神经网络
不过一般便于我们把网络在时间上逐步的行为在空间上展示,我们常常这样作图:
现在我们来看一句话,单看后面
,我们会认为“老虎”、“扬子鳄”、“袋鼠”等等都是合理的。
但是根据前面的四川
,我会认为这个词大概率是“熊猫”
,而不是其他动物。
此时我们发现,“四川”这个词距离后面填空非常远,换句话说:依赖的路径十分的长
,标准的RNN结构在这种“长依赖”
问题上表现并不好。所以人们又对神经网络进行了改造,其中比较著名的便是:LSTM
这是一个标准的RNN结构的某一步:
我们再来看看LSTM结构:
LSTM结构中的输出再次经过一个tanh函数,而原先的输出则变成了一个叫细胞状态的东西,这个细胞状态就是LSTM结构能应对长依赖问题的关键,这个结构的输入相应的变成了上一个细胞状态和上一步的输出。为了实现记忆和遗忘,LSTM结构使用了两个门来实现:遗忘门、更新门。最终的输出也有一个输出门,根据输出的内容的重要性进行强弱输出。
🔗有关LSTM的更多理解,可以参考这篇博客:《Understanding LSTM Networks》
大家可以了解一下,相比于LSTM,GRU做了哪些改变和简化。
2 编程实验
参考链接:关于第三方预训练词向量的下载方法
项目演示为红框内容(这个1.7G的预训练词向量文件的加载过程十分的长):
词向量处理工具:chinese_vec.py
import os import numpy as np def load_word_vecs(): embeddings_index = {} f = open(os.path.dirname(os.path.abspath(__file__)) + '/sgns.target.word-word.dynwin5.thr10.neg5.dim300.iter5', encoding='utf8') f.readline() # escape first line for line in f: values = line.split() word = values[0] coefs = np.asarray(values[1:], dtype='float32') embeddings_index[word] = coefs f.close() print('Found %s word vectors.' % len(embeddings_index)) return embeddings_index
一份网购评论数据:online_shopping_10_cats.csv
cat,label,review 书籍,1,做父母一定要有刘墉这样的心态,不断地学习,不断地进步,不断地给自己补充新鲜血液,让自己保持一颗年轻的心。我想,这是他能很好的和孩子沟通的一个重要因素。读刘墉的文章,总能让我看到一个快乐的平易近人的父亲,他始终站在和孩子同样的高度,给孩子创造着一个充满爱和自由的生活环境。很喜欢刘墉在字里行间流露出的做父母的那种小狡黠,让人总是忍俊不禁,父母和子女之间有时候也是一种战斗,武力争斗过于低级了,智力较量才更有趣味。所以,做父母的得加把劲了,老思想老观念注定会一败涂地,生命不息,学习不止。家庭教育,真的是乐在其中。 书籍,1,作者真有英国人严谨的风格,提出观点、进行论述论证,尽管本人对物理学了解不深,但是仍然能感受到真理的火花。整本书的结构颇有特点,从当时(本书写于八十年代)流行的计算机话题引入,再用数学、物理学、宇宙学做必要的铺垫——这些内容占据了大部分篇幅,最后回到关键问题:电脑能不能代替人脑。和现在流行的观点相反,作者认为人的某种“洞察”是不能被算法模拟的。也许作者想说,人的灵魂是无可取代的。 书籍,1,作者长篇大论借用详细报告数据处理工作和计算结果支持其新观点。为什么荷兰曾经县有欧洲最高的生产率?为什么在文化上有着深刻纽带关系的中国和日本却在经济发展上有着极大的差异?为什么英国的北美殖民地造就了经济强大的美国,而西班牙的北美殖民却造就了范后的墨西哥?……很有价值,但不包括【中国近代史专业】。 书籍,1,作者在战几时之前用了"拥抱"令人叫绝.日本如果没有战败,就有会有美军的占领,没胡官僚主义的延续,没有战后的民发反思,没有~,就不会让日本成为一个经济强国.当然,美国人也给日本人带来了耻辱.对日中关系也造成了深远的影响.文中揭露了"东京审判"中很多鲜为人知的东西.让人惊醒.唉!中国人民对日本的了解是不是太少了. 书籍,1,作者在少年时即喜阅读,能看出他精读了无数经典,因而他有一个庞大的内心世界。他的作品最难能可贵的有两点,一是他的理科知识不错,虽不能媲及罗素,但与理科知识很差的作家相比,他的文章可读性要强;其二是他人格和文风的朴实,不造作,不买弄,让人喜欢。读他的作品,犹如听一个好友和你谈心,常常唤起心中的强烈的共鸣。他的作品90年后的更好些。衷心祝愿周国平健康快乐,为世人写出更多好作品。 书籍,1,作者有一种专业的谨慎,若能有幸学习原版也许会更好,简体版的书中的印刷错误比较多,影响学者理解,全书结构简单,但内容详实,学起来如鱼得水非常轻松。这只是一项技术而已,若可以结合本专业,将会得到更高的学习快乐,家财万贯不如一技在身,一技在身不如一念在心,本书有不仅有技,而且有念。书中佳品。 ... 完整版在文末 每条数据有三个部分:商品分类、情感标签数据(1正面、0负面)、评论文本
数据操作工具:shopping_data.py
import os import keras import numpy as np import keras.preprocessing.text as text import re import jieba import random def load_data(): xs = [] ys = [] with open(os.path.dirname(os.path.abspath(__file__))+'/online_shopping_10_cats.csv','r',encoding='utf-8') as f: line=f.readline()#escape first line"label review" while line: line=f.readline() if not line: break contents = line.split(',') # if contents[0]=="书籍": # continue label = int(contents[1]) review = contents[2] if len(review)>20: continue xs.append(review) ys.append(label) xs = np.array(xs) ys = np.array(ys) #打乱数据集 indies = [i for i in range(len(xs))] random.seed(666) random.shuffle(indies) xs = xs[indies] ys = ys[indies] m = len(xs) cutpoint = int(m*4/5) x_train = xs[:cutpoint] y_train = ys[:cutpoint] x_test = xs[cutpoint:] y_test = ys[cutpoint:] print('总样本数量:%d' % (len(xs))) print('训练集数量:%d' % (len(x_train))) print('测试集数量:%d' % (len(x_test))) return x_train,y_train,x_test,y_test def createWordIndex(x_train,x_test): x_all = np.concatenate((x_train,x_test),axis=0) #建立词索引 tokenizer = text.Tokenizer() #create word index word_dic = {} voca = [] for sentence in x_all: # 去掉标点 sentence = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()]+", "", sentence) # 结巴分词 cut = jieba.cut(sentence) #cut_list = [ i for i in cut ] for word in cut: if not (word in word_dic): word_dic[word]=0 else: word_dic[word] +=1 voca.append(word) word_dic = sorted(word_dic.items(), key = lambda kv:kv[1],reverse=True) voca = [v[0] for v in word_dic] tokenizer.fit_on_texts(voca) print("voca:"+str(len(voca))) return len(voca),tokenizer.word_index def word2Index(words,word_index): vecs = [] for sentence in words: # 去掉标点 sentence = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()]+", "", sentence) # 结巴分词 cut = jieba.cut(sentence) #cut_list = [ i for i in cut ] index=[] for word in cut: if word in word_index: index.append(float(word_index[word])) # if len(index)>25: # index = index[0:25] vecs.append(np.array(index)) return np.array(vecs)
模型训练:comments_lstm.py
import shopping_data # 数据对齐 from keras.utils import pad_sequences from keras.models import Sequential from keras.layers import Dense, Embedding # 导入LSTM from keras.layers import LSTM # 读取中文词向量工具 import chinese_vec import numpy as np x_train, y_train, x_test, y_test = shopping_data.load_data() # 打印数据集 # print('x_train.shape:', x_train.shape) # print('y_train.shape:', y_train.shape) # print('x_test.shape:', x_test.shape) # print('y_test.shape:', y_test.shape) # print(x_train[0]) # print(y_train[0]) vocalen, word_index = shopping_data.createWordIndex(x_train, x_test) # print(word_index) # print('词典总词数:', vocalen) # 转化为索引向量 x_train_index = shopping_data.word2Index(x_train, word_index) x_test_index = shopping_data.word2Index(x_test, word_index) # 每一句话的索引向量个数不一样,我们需要把序列按照maxlen对齐 maxlen = 25 x_train_index = pad_sequences(x_train_index, maxlen=maxlen) x_test_index = pad_sequences(x_test_index, maxlen=maxlen) # 自行构造词嵌入矩阵 word_vecs = chinese_vec.load_word_vecs() embedding_matrix = np.zeros((vocalen, 300)) for word, i in word_index.items(): embedding_vector = word_vecs.get(word) if embedding_vector is not None: embedding_matrix[i] = embedding_vector # 神经网络模型 model = Sequential() model.add( Embedding( trainable=False, # 冻结这一层,不要让它预训练 weights=[embedding_matrix], input_dim=vocalen, # 输入维度 output_dim=300, # 输出维度 input_length=maxlen # 序列长度 ) ) model.add(LSTM( 128, # 输出数据的维度 return_sequences=True # 每一个都输出结果 )) model.add(LSTM(128)) # 二分类问题,使用sigmoid激活函数 model.add(Dense(1, activation='sigmoid')) model.compile( loss='binary_crossentropy', # 适用于二分类问题的交叉熵代价函数 optimizer='adam', # adam是一种使用动量的自适应优化器,比普通的sgd优化器更快 metrics=['accuracy'] ) # 训练 model.fit(x_train_index, y_train, batch_size=512, epochs=200) score, acc = model.evaluate(x_test_index, y_test) # 评估 print('Test score:', score) print('Test accuracy:', acc)