29、深度学习-自学之路-深入理解-NLP自然语言处理-做一个完形填空,让机器学习更多的内容程序展示
import sys,random,math from collections import Counter import numpy as np np.random.seed(1) random.seed(1) f = open('reviews.txt') raw_reviews = f.readlines() f.close() tokens = list(map(lambda x:(x.split(" ")),raw_reviews)) #wordcnt = Counter() 这行代码的作用是创建一个 Counter 对象, # 并将其赋值给变量 wordcnt。Counter 是 Python 标准库 collections 模块中的一个类, # 它是一种特殊的字典,用于对可哈希对象进行计数。 wordcnt = Counter() for sent in tokens: for word in sent: wordcnt[word] -= 1 vocab = list(set(map(lambda x:x[0],wordcnt.most_common()))) #print(vocab) word2index = {} for i,word in enumerate(vocab): word2index[word]=i concatenated = list() input_dataset = list() for sent in tokens: sent_indices = list() for word in sent: try: sent_indices.append(word2index[word]) concatenated.append(word2index[word]) except: "" input_dataset.append(sent_indices) ''' concatenated = np.array(concatenated) 这行代码的主要功能是将 concatenated 对象转换为 NumPy 数组。 NumPy 是 Python 中用于科学计算的一个重要库,np.array() 是 NumPy 提供的一个函数, 它可以将多种类型的对象(如列表、元组等)转换为 NumPy 数组,以便进行高效的数值计算和操作。 ''' concatenated = np.array(concatenated) ''' random.shuffle(input_dataset) 这行代码的主要功能是对 input_dataset 列表中的元素进行随机打乱顺序的操作。 random 是 Python 的标准库,shuffle 是 random 模块中的一个函数,它会直接修改传入的列表,使其元素顺序随机化。 ''' random.shuffle(input_dataset) alpha, iterations = (0.05, 10) hidden_size,window,negative = (50,2,5) weights_0_1 = (np.random.rand(len(vocab),hidden_size) - 0.5) * 0.2 weights_1_2 = np.random.rand(len(vocab),hidden_size)*0 layer_2_target = np.zeros(negative+1) layer_2_target[0] = 1 ''' 下面的这个程序要表现的东西会很多,请大家慢慢听我描述 程序功能是: 这段 Python 代码定义了一个名为 similar 的函数,其主要目的是找出与给定目标单词(默认是 'beautiful') 在词向量空间中最相似的 10 个单词。它基于词向量之间的欧几里得距离来衡量单词间的相似度。 然后我们怎么找到词向量,然后什么又是欧几里得距离: 先说:词向量。我们都知道当我们对一个神经网络训练完成以后,我们就会得到一组权重。这组权重一般是如果输入层是n个值,隐藏层是m个点。 那么这个权重就是(n,m)的一个numpy矩阵。 对应的权重矩阵的第一行第一列就是我们说的第一个输入元素的词向量。 然后我们说一下什么是欧几里得距离,计算公式我知道: raw_difference = weights_0_1[index] - (weights_0_1[target_index]) squared_difference = raw_difference * raw_difference scores[word] = -math.sqrt(sum(squared_difference)) 使用全部词的词向量值和 beautiful这个词的词向量值进行相减。 然后在把相减后的结果进行平方 然后再把所有的平方的数据相加然后求平方根。 然后 ''' def similar(target='beautiful'): target_index = word2index[target] scores = Counter() ''' for word, index in word2index.items()::遍历 word2index 字典中的每个键值对,word 是单词, index 是该单词在词向量矩阵中的索引。 ''' for word,index in word2index.items(): ''' raw_difference = weights_0_1[index] - (weights_0_1[target_index]): 计算当前单词的词向量与目标单词的词向量之间的差值。weights_0_1 是一个二维的 NumPy 数组, 存储着所有单词的词向量,每一行对应一个单词的词向量。 ''' raw_difference = weights_0_1[index] - (weights_0_1[target_index]) ''' squared_difference = raw_difference * raw_difference:对差值向量的每个元素进行平方操作。 ''' squared_difference = raw_difference * raw_difference ''' scores[word] = -math.sqrt(sum(squared_difference)):计算平方和的平方根,得到欧几里得距离, 并取其负值作为相似度得分。之所以取负值,是因为 Counter 的 most_common 方法会返回得分最高的元素, 而我们希望距离最近(相似度最高)的单词排在前面。 ''' scores[word] = -math.sqrt(sum(squared_difference)) '''most_common(10) 方法会返回 scores 中得分最高的 10 个元素及其得分, 以列表形式呈现,列表中的每个元素是一个包含单词和得分的元组。 ''' return scores.most_common(10) def sigmoid(x): return 1/(1 + np.exp(-x)) for rev_i, review in enumerate(input_dataset * iterations): for target_i in range(len(review)): # since it's really expensive to predict every vocabulary # we're only going to predict a random subset ''' 一个正样本,加上5给随机负样本。因为负样本太多了,这个里面采用了随机负样本的方式来进行训练,减少整个程序的运行算力。 ''' target_samples = [review[target_i]] + list(concatenated \ [(np.random.rand(negative) * len(concatenated)).astype( 'int').tolist()]) #print("target_samples") #print(target_samples) #提取正样本前面的2个单词, left_context = review[max(0, target_i - window):target_i] # 提取正样本后面的2个单词, right_context = review[target_i + 1:min(len(review), target_i + window)] #中间隐藏层的计算方法是把所有对应的输入都相加,然后进行平均。CBOW的处理方式。 layer_1 = np.mean(weights_0_1[left_context + right_context], axis=0) #在CBOW里面使用的是softmax作为最后输出层的函数。 layer_2 = sigmoid(layer_1.dot(weights_1_2[target_samples].T)) #计算损失函数 layer_2_delta = layer_2 - layer_2_target #计算第一层的损失函数 layer_1_delta = layer_2_delta.dot(weights_1_2[target_samples]) #进行第一层的权重更新 weights_0_1[left_context + right_context] -= layer_1_delta * alpha #进行第二层的权重更新 weights_1_2[target_samples] -= np.outer(layer_2_delta, layer_1) * alpha if (rev_i % 250 == 0): sys.stdout.write('\rProgress:' + str(rev_i / float(len(input_dataset) * iterations)) + " " + str(similar('terrible'))) sys.stdout.write('\rProgress:' + str(rev_i / float(len(input_dataset) * iterations))) print(similar('terrible')) #输出与terrible相近的词向量。 terrible:糟糕的、可怕的 #运行结果 有4个词和terrible的词义是相近的。一共10个词,只有4个词是相近的。说明这个模型非常的失败。 #主要的原因我考虑是不是训练的太少 ''' 训练了2次,整个数据集。 Progress:0.99998 [('terrible', -0.0), #可怕的 ('horrible', -2.7898821106802045), #令人恐惧的 ('brilliant', -3.470232426351145), #明亮的,恒成功的 ('pathetic', -3.8372465135492355), #可怜的 ('phenomenal', -3.928297271813787), #非凡的 ('mediocre', -3.9289917580116294), #平庸的 ('superb', -3.9764853704721492), #棒 极 了 ('masterful', -4.022889507518986), #有驾驭能力的 ('marvelous', -4.0699092654045375), #了不起的 ('bad', -4.220448952264187)] #坏的 ''' ''' 这个里面只是给出了一个相关的程序,但是还有很多需要改进的地方,因为没有一个合适的数据集。用的这个数据集也很差。 如果我们想要训练一个真的相关的完形填空。然后要去考察对应的值。那么我们应该使用和正面或者反面有关的数据来进行训练。 因为现在只是用这个数据集,训练的时候用的是5个一组的数据,这个就很难受。很难做出更好的数据。导致最后的识别率差不多是一半一半。基本上没有识别能力。 应该像小孩子学习语文的时候一样,同义词反义词,该怎么填写,是填写好,还是填写坏。要有对应的语境,才能更好的训练出来。 就如同我们要做一个什么人?这个语境下要填入好的概率就非常高。 或者是我们喜欢和什么人玩,这个也是填写好人更好。 感觉作者在这个地方的时候,已经有点糊弄了。 但是还是感谢作者,因为我们有一个好的收获。因为我们已经大概知道了整体的运行效果。和程序的关键思路。 ''' '''循环训练了10次,还是这个结果,说明这个模型确实是有问题的。 Progress:0.999996[ ('terrible', -0.0), #糟糕的、可怕的 ('brilliant', -3.4459636758170444), #明亮的 ('horrible', -3.6093847255874367), #令人恐惧的 ('dreadful', -3.661559427113935), #可怕的 ('pathetic', -3.705108491197224), #可怜的 ('superb', -3.735478039156396), 棒 极 了 ('bad', -3.790820154323175), 坏的 ('marvelous', -3.864905791871544), 了不起的 ('remarkable', -3.897655193993103), 非凡的 ('fantastic', -4.041131904413315)] 好极了 '''
这个里面有我对所有的代码的注释:可以帮助我们理解整个程序,这个程序是有问题的,但是整个思路是对的。因为多次循环后,还是不能找到正确的关系。说明输入和输出是有问题的。我后续可能会把这个程序整改一下。