词义的处理上有不同的方式

我们现在使用的RNN, CBOW,Skip-Gram

RNN 是一类专门用于处理序列数据的神经网络。与传统的前馈神经网络不同,RNN 引入了循环结构,允许网络在处理当前输入时,能够记住之前的信息。具体来说,RNN 在每个时间步接收输入并生成输出,同时将当前时间步的隐藏状态传递到下一个时间步,使得网络可以对序列中的信息进行建模

应用场景

  • 语言模型:预测下一个单词的概率。
  • 机器翻译:将一种语言的句子翻译成另一种语言。
  • 语音识别:将语音信号转换为文本。

CBOW

原理

CBOW 是一种用于学习词向量的无监督学习模型。它的基本思想是根据一个词的上下文(即周围的词)来预测这个词。具体来说,CBOW 模型将上下文词的词向量进行求和或平均,然后通过一个线性变换和 softmax 函数来预测中心词的概率。模型的训练目标是最大化预测中心词的概率。

应用场景
  • 词向量学习:生成词的分布式表示,用于后续的 NLP 任务,如文本分类、情感分析等。
  • 语义相似度计算:通过比较词向量的相似度来判断词语之间的语义关系。
优点
  • 训练速度快,因为它只需要根据上下文预测一个中心词。
  • 对于低频词的处理效果较好,因为它可以从上下文中学习到低频词的信息。

3. Skip - Gram 模型

原理

Skip - Gram 也是一种用于学习词向量的无监督学习模型,与 CBOW 相反,Skip - Gram 模型根据一个中心词来预测它的上下文。具体来说,模型接收一个中心词的词向量作为输入,然后通过一个线性变换和 softmax 函数来预测上下文词的概率。模型的训练目标是最大化预测上下文词的概率。

应用场景

  • 词向量学习:与 CBOW 类似,Skip - Gram 也可以用于生成词的分布式表示。
  • 推荐系统:通过学习物品之间的语义关系,为用户推荐相关的物品。
优点

  • 能够更好地捕捉词与词之间的语义关系,尤其是对于语义相近的词。
  • 对于生僻词的学习效果较好,因为它可以通过中心词来学习生僻词的上下文信息。

三者比较

  • RNN 与 CBOW、Skip - Gram:RNN 主要用于处理序列数据,关注序列中信息的顺序和上下文关系,可用于多种序列建模任务;而 CBOW 和 Skip - Gram 主要用于学习词向量,侧重于捕捉词语之间的语义关系。
  • CBOW 与 Skip - Gram:CBOW 根据上下文预测中心词,训练速度快,对低频词处理较好;Skip - Gram 根据中心词预测上下文,能更好地捕捉语义关系,对生僻词学习效果较好。

程序如下:

import sys,random,math
from collections import Counter
import numpy as np
#读取文件
f = open('tasks_1-20_v1/en/qa1_single-supporting-fact_train.txt','r')
raw = f.readlines()
f.close()

#把文件中的前1000条,出去空格,出去换行,单独的单词列出来
tokens = list()
for line in raw[0:1000]:
    tokens.append(line.lower().replace("\n","").split(" ")[1:])

#打印前四条 [['mary', 'moved', 'to', 'the', 'bathroom.'], ['john', 'went', 'to', 'the', 'hallway.'], ['where', 'is', 'mary?', '\tbathroom\t1']]
print(tokens[0:3])

vocab = list(set(word for sent in tokens for word in sent))
vocab_size = len(vocab)

#把tokens 中的数据都放到vocab链表中
vocab = set()
for sent in tokens:
    for word in sent:
        vocab.add(word)

#整理成一个整个链表
word2indexvocab = list(vocab)
print("vocab")
print(vocab)
'''
{'hallway.', 'back', '\tbedroom\t2', 'bathroom.', '\toffice\t5', '\thallway\t11', '\thallway\t13', '\tkitchen\t11', '\tbathroom\t2', '\tgarden\t10', 'garden.', 'sandra', '\tbedroom\t1', '\tgarden\t4', '\tbedroom\t5', '\tkitchen\t5', '\thallway\t1', 'daniel', '\tbathroom\t8', 'john?', '\tkitchen\t1', '\tbathroom\t4', '\thallway\t10', '\tgarden\t13', 'where', '\thallway\t5', 'went', '\tgarden\t8', '\tbathroom\t13', '\tkitchen\t13', '\tbedroom\t11', 'bedroom.', 'is', '\tkitchen\t7', 'office.', '\thallway\t7', 'moved', '\toffice\t2', '\tkitchen\t10', '\tbedroom\t13', '\toffice\t14', '\tbathroom\t11', '\toffice\t1', 'mary?', '\tbathroom\t7', '\tgarden\t5', '\tbedroom\t10', 'john', '\tgarden\t2', '\toffice\t10', '\tbedroom\t8', '\tkitchen\t8', 'sandra?', '\tbathroom\t1', 'daniel?', 'journeyed', '\tgarden\t1', 'the', '\tbathroom\t5', '\tgarden\t14', '\tbathroom\t10', '\thallway\t4', 'travelled', '\tgarden\t11', '\tbedroom\t14', '\thallway\t8', '\thallway\t14', '\tkitchen\t4', '\tkitchen\t2', '\thallway\t2', 'mary', '\tbedroom\t4', '\toffice\t11', 'kitchen.', '\toffice\t8', '\toffice\t13', '\tbedroom\t7', 'to', '\tbathroom\t14', '\tgarden\t7', '\tkitchen\t14', '\toffice\t4'}
'''
#给vocab链表中的每个单词一个序号
word2index = {}
for i, word in enumerate(vocab):
    word2index[word] = i
print("word2index")
print(word2index)
'''
word2index
{'hallway.': 0, 'back': 1, '\tbedroom\t2': 2, 'bathroom.': 3, '\toffice\t5': 4, '\thallway\t11': 5, 
'\thallway\t13': 6, '\tkitchen\t11': 7, '\tbathroom\t2': 8, '\tgarden\t10': 9, 'garden.': 10, 
'sandra': 11, '\tbedroom\t1': 12, '\tgarden\t4': 13, '\tbedroom\t5': 14, '\tkitchen\t5': 15, 
'\thallway\t1': 16, 'daniel': 17, '\tbathroom\t8': 18, 'john?': 19, '\tkitchen\t1': 20, '\tbathroom\t4': 21, 
'\thallway\t10': 22, '\tgarden\t13': 23, 'where': 24, '\thallway\t5': 25, 'went': 26, '\tgarden\t8': 27, 
'\tbathroom\t13': 28, '\tkitchen\t13': 29, '\tbedroom\t11': 30, 'bedroom.': 31, 'is': 32, 
'\tkitchen\t7': 33, 'office.': 34, '\thallway\t7': 35, 'moved': 36, '\toffice\t2': 37, '\tkitchen\t10': 38,
 '\tbedroom\t13': 39, '\toffice\t14': 40, '\tbathroom\t11': 41, '\toffice\t1': 42, 'mary?': 43, 
 '\tbathroom\t7': 44, '\tgarden\t5': 45, '\tbedroom\t10': 46, 'john': 47, '\tgarden\t2': 48, 
 '\toffice\t10': 49, '\tbedroom\t8': 50, '\tkitchen\t8': 51, 'sandra?': 52, '\tbathroom\t1': 53, 
 'daniel?': 54, 'journeyed': 55, '\tgarden\t1': 56, 'the': 57, '\tbathroom\t5': 58, '\tgarden\t14': 59, 
 '\tbathroom\t10': 60, '\thallway\t4': 61, 'travelled': 62, '\tgarden\t11': 63, '\tbedroom\t14': 64,
  '\thallway\t8': 65, '\thallway\t14': 66, '\tkitchen\t4': 67, '\tkitchen\t2': 68, '\thallway\t2': 69,
   'mary': 70, '\tbedroom\t4': 71, '\toffice\t11': 72, 'kitchen.': 73, '\toffice\t8': 74, 
   '\toffice\t13': 75, '\tbedroom\t7': 76, 'to': 77, '\tbathroom\t14': 78, '\tgarden\t7': 79, 
   '\tkitchen\t14': 80, '\toffice\t4': 81}
'''

#返回链表索引
'''
sentence = ['apple', 'banana', 'cherry'],
并且word2index字典中'apple': 1,'banana': 2,'cherry': 3,
那么调用words2indices(sentence)将返回[1, 2, 3]。
'''
def words2indices(sentence):
    idx = list()
    for word in sentence:
        idx.append(word2index[word])
    return idx

#定义一个softmanx的处理函数
def softmax(x):
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0)

#固定初始化的随机权重值
np.random.seed(1)

#词向量维度
embed_size = 10

# word embeddings 初始化词向量维度的值,
embed = (np.random.rand(len(vocab),embed_size) - 0.5) * 0.1

#定义一个embed_size*embed_size的单位矩阵 循环权重矩阵 recurrent
# embedding -> embedding (initially the identity matrix)
recurrent = np.eye(embed_size)

#start 初始化初始的向量 起始向量 start
# sentence embedding for empty sentence
start = np.zeros(embed_size)


# embedding -> output weights 输出权重矩阵 decoder
decoder = (np.random.rand(embed_size, len(vocab)) - 0.5) * 0.1

#单位矩阵 one_hot。
# one hot lookups (for loss function)
one_hot = np.eye(len(vocab))


def predict(sent):
    layers = list()  #一个空列表,用于存储每个时间步的状态信息。
    layer = {}       #一个字典,用于存储当前时间步的状态。初始时,将 start(应该是一个预先定义好的初始隐藏状态向量)赋值给 layer['hidden'],并将该状态添加到 layers 列表中。
    layer['hidden'] = start
    layers.append(layer)

    loss = 0 #loss:初始化为 0,用于累计预测损失。

    # forward propagate
    preds = list()
    for target_i in range(len(sent)):
        layer = {}

        # try to predict the next term
        layer['pred'] = softmax(layers[-1]['hidden'].dot(decoder))
        #对下一个词进行预测,每次都进行预测的原因是你不知道那个词是最后一个,也就是最后你需要预测的那个词,所以每次都进行预测。
        # 直到最后的那个预测。

        #计算误差值
        loss += -np.log(layer['pred'][sent[target_i]])

        #更新新的隐藏层
        # generate the next hidden state
        layer['hidden'] = layers[-1]['hidden'].dot(recurrent) + embed[sent[target_i]]
        layers.append(layer)

    return layers, loss

# forward
for iter in range(30000):
    alpha = 0.001
    sent = words2indices(tokens[iter%len(tokens)][1:]) #选择一句文本中的句子进行预测

    layers,loss = predict(sent)  #对这个句子进行预处理,把要预测的值提取出来,把每一层分层分时相加。

    # back propagate
    for layer_idx in reversed(range(len(layers))):
        layer = layers[layer_idx]
        target = sent[layer_idx-1]

        if(layer_idx > 0):
            layer['output_delta'] = layer['pred'] - one_hot[target]
            '''
            这里的真实值使用的还是one-hot的数据模型,不过数据少的时候,这样做是没有问题的。
            '''
            new_hidden_delta = layer['output_delta'].dot(decoder.transpose())
            '''
            decoder 是一个矩阵,它的形状通常是 (隐藏层维度, 输出层维度),表示从隐藏层到输出层的权重矩阵。
            decoder.transpose() 是 decoder 的转置矩阵,形状变为 (输出层维度, 隐藏层维度)。
            '''

            # if the last layer - don't pull from a
            # later one becasue it doesn't exist
            if(layer_idx == len(layers)-1): #如果是最后一层,不要从后面拉取层,因为不存在
                layer['hidden_delta'] = new_hidden_delta
            else:
                layer['hidden_delta'] = new_hidden_delta + layers[layer_idx+1]['hidden_delta'].dot(recurrent.transpose())
        else:#如果是第一层
            layer['hidden_delta'] = layers[layer_idx+1]['hidden_delta'].dot(recurrent.transpose())
    # update weights
    start -= layers[0]['hidden_delta'] * alpha / float(len(sent))
    for layer_idx, layer in enumerate(layers[1:]):
        #对隐藏层进行更新,输出层的权重进行跟新,常规操作,可以理解。
        decoder -= np.outer(layers[layer_idx]['hidden'], layer['output_delta']) * alpha / float(len(sent))
        #
        embed_idx = sent[layer_idx]
        #对对应的中间层进行跟新。
        embed[embed_idx] -= layers[layer_idx]['hidden_delta'] * alpha / float(len(sent))
        #对单位举证向量也进行更新,这个很重要,这个已经对单词是怎么走的做出来了自己更新。
        #但是有一个问题,单位矩阵会使用在很多层上的,如果改变了,不是每一次层单独改变的,是一起改变的。会不会有其他的影响。
        recurrent -= np.outer(layers[layer_idx]['hidden'], layer['hidden_delta']) * alpha / float(len(sent))

    if (iter % 1000 == 0):
        print("Perplexity:" + str(np.exp(loss / len(sent))))

sent_index = 4

l,_ = predict(words2indices(tokens[sent_index]))

print(tokens[sent_index])

for i,each_layer in enumerate(l[1:-1]):
    input = tokens[sent_index][i]
    true = tokens[sent_index][i+1]
    pred = vocab[each_layer['pred'].argmax()]
    print("Prev Input:" + input + (' ' * (12 - len(input))) +\
          "True:" + true + (" " * (15 - len(true))) + "Pred:" + pred)

'''

核心思想是RNN 通过将一个句子中的每个词的输入顺序进行加入影响因素中,这样更像在表达一个正常的话,而不是仅仅是概率上的分析,也可以对 韩国队打败日本队和日本队打败韩国队,这样的输入进行混淆。

可以在一定程度上真正的理解训练的这段话是什么意思了。

'''

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐