tensorflow实现Skip-Gram模型

本文使用tensorflow实现skim-gram模型

导入相关库

import tensorflow as tf
import zipfile
from collections import Counter
import numpy as np
import random

对数据进行解压以及读取数据

zip_file_path = './embedding/text8.zip'
des_folder_path = './data/'
with zipfile.ZipFile(zip_file_path) as zip_ref:
    zip_ref.extractall(des_folder_path)
with open('./data/text8') as f:
    text = f.read()

对数据进行预处理

对数据进行预处理,主要是包括低频词剔除,高频词剔除,同时将标点替换,高频词进行采样

text = text.lower()  # 将字符串全部变为小写,避免同一个单词大小写问题
# 转化标点符号,因为在我们切分的时候,英文是用空格切分,但是标点符号会出现前后没有空格,那么标点前和后就会切分为一个单词了,或者标点
#前没有空格,后面有空格,那么标点就跟着前面的单词切分到一起了,具体看个例子
# x = 'I come from china,how about you?'
# x.split() 
# 结果为['I', 'come', 'from', 'china,how', 'about', 'you?'],这样单词和标点到一起了
# 同时注意细节,我们在替换标点的时候,前后会多一个空格,也是为了不和标点前后单词切分在一起
text = text.replace('.', ' <PERIOD> ')
text = text.replace(',', ' <COMMA> ')
text = text.replace('"', ' <QUOTATION_MARK> ')
text = text.replace(';', ' <SEMICOLON> ')
text = text.replace('!', ' <EXCLAMATION_MARK> ')
text = text.replace('?', ' <QUESTION_MARK> ')
text = text.replace('(', ' <LEFT_PAREN> ')
text = text.replace(')', ' <RIGHT_PAREN> ')
text = text.replace('--', ' <HYPHENS> ')
text = text.replace('?', ' <QUESTION_MARK> ')
text = text.replace('\n', ' <NEW_LINE> ')
text = text.replace(':', ' <COLON> ')
# 因为读取出来的是一个字符串,我们需要切分成一个一个单词
words = text.split()
# 在这里,我们需要剔除低频词,也是相当于降低噪声,我们设置频次低于5的我们直接剔除
word_fre = Counter(words)
words = [word for word in words if word_fre[word]>5]

构建单词映射字典

# 得到words以后,我们需要构建词和数之间的一一映射表
set_words = set(words)
word_to_int = {value:key for key,value in enumerate(set_words)}
int_to_word = {value:key for key,value in word_to_int.items()}

高频词的处理,对于高频词,在大的语料库中比如the is 等会出现上万次,但是这些词带有的有用的信息却很少,所以这些词,我们采用一个subsample的方法 ,我们和谷歌开源的word2vec工具采用的方式一样,具体如下

P(w)=1(tf(w)+tf(w))

f(w) 表示该词频率
t 为一个阈值,需要我们给定
P(w) 表示该单词被舍弃的概率

# 设置一个阈值
t = 1e-5
# 统计词频
word_fre = Counter(words)
# 构建每个词对应的舍弃的概率的字典
words_pro_dict = {word:1-(np.sqrt(t*len(words)/word_fre[word])+t*len(words)/word_fre[word]) for word in words}
# 对每个词进行采样
words = [word for word in words if np.random.rand() > words_pro_dict[word]]

将词序变为数值型序列

# 将单词序列变为数值序列
int_word = [word_to_int[word] for word in words]

构造训练数据

这部分是为输入做准备

def get_y(batch,ind,window_size=5):
    # batch 为我们batch_size里面的单词
    # ind 表示当前词在batch中的位置
    # window_size 为窗口大小,也就是我们取当前单词前后多少个词,那么也就是总共去2×window_size个词
    # 因为我们知道,应该说离得越近,单词之间的相关性越高,此处采用一个随机窗口大小也是这个目的,让我们尽量
    # 训练的时候多使用越近的词
    window_size = np.random.randint(1,window_size+1)
    # 当我们当前单词比较靠前,索引小于window_size时,我们取当前单词前的单词,和当前单词后的window_size个词
    # 注意我们并不是说一定取2×window_size个词,而是严格按照前后各window_size个,如果前不够不往后补,后不够也不
    # 前补,举一个例子,batch=20,当前单词为3,window_size=5,那么返回的是前batch[:3]+batch[i+window_size],
    # 而不是batch[:i]+batch[i:2*window_size-i],后面不够也是一样的道理
    if ind<window_size:
        # 以下使用set是去掉重复的词,
        return list(set(batch[:ind] + batch[ind:window_size+ind]))
    # 这个地方不需要在细分当len(batch)-i<window_size的情况,也就是当前单词比较靠后的时候,这种情况可以和在中间情况合在一起的原因是
    # python中的list,比如x=[1,2,5,6,7,8],那么x[3:100]和x[3:] 其实是一样的,并不会报索引越界的错误。
    else:
        return list(set(batch[ind-window_size:ind] + batch[ind:window_size+ind]))


def get_batch(int_word,batch_size,window_size=5):
    # 计算总共有多少个batch
    n_batch = len(int_word)//batch_size
    # 此处我们把不足batch_size的舍弃
    int_word = int_word[:n_batch*batch_size]

    for j in range(0,len(int_word),batch_size):
        x_batch = []
        y_batch = []
        batch_word = int_word[j:j+batch_size]
        for n,m in enumerate(batch_word):
            x_b = m
            y_b = get_y(batch_word,n,window_size)
            x_batch.extend([x_b]*len(y_b))
            y_batch.extend(y_b)
        yield x_batch,y_batch

构建模型

定义输入的placeholder

x_input = tf.placeholder(tf.int32,[None],name='input')
y_label = tf.placeholder(tf.int32,[None,None],name='label')

定义参数

embedding_size = 300  # 词嵌入的维度,也就是最后每个词对应一个1×300的向量
vocab_size = len(int_to_word)
num_sampled = 100
lr = 0.001
epochs = 100 # 训练的次数
batch_size = 1000
window_size = 10
top_n = 8 # top_n表示我们最终选择打印词最相近的top_n个

定义loss等

# 定义隐藏层的权重大小
weight_emb = tf.Variable(tf.random_uniform([vocab_size,embedding_size],-1,1),name='weight_emb')
# tf.nn.embedding_lookup是选取张量weight_emb中对应x_input索引的值,
embed_vec = tf.nn.embedding_lookup(weight_emb,x_input)
# 输出层
# 
out_w = tf.Variable(tf.truncated_normal([vocab_size, embedding_size],mean=0,stddev=0.1))
out_b = tf.Variable(tf.zeros(vocab_size))
# tf.nn.sampled_softmax_loss 这个是tensorflow提供的候选采样的一种方式,也有其他的很多方法,主要是因为对于这种文本
# 类别太多了,训练过程非常慢,所以才有候选采样的方式,也就是每次只在一小部分样本上更新
loss = tf.reduce_mean(tf.nn.sampled_softmax_loss(out_w,out_b,y_label,embed_vec,num_sampled,vocab_size))
optimizer = tf.train.AdamOptimizer(learning_rate=lr).minimize(loss)

定义我们的验证词,我们把验证的词前top_n相近的词打印出来

# 为了展示输出的效果,我们在训练的时候打印一些信息
# 以下是求weight_emb的每个行的模长,但我们知道,其实他的每一行就对应一个词,我们把这些词对应的向量的模长求出来,
# 然后将每个词对应的词向量变为单位向量,这样我们使用embedding_lookup取出词也是单位向量,那么计算余弦距离就可以
# 直接矩阵相乘,得到我们所要计算词的余弦距离,然后我们在排序就可以取前几个最相似的词
norm = tf.sqrt(tf.reduce_sum(tf.square(weight_emb),axis=1,keep_dims=True))
norm_embedd = weight_emb / norm
# 随机选择8个词作为我们计算最相近的词
val_data = random.choices(words,k=top_n)
val_int_data = tf.constant([word_to_int[i] for i in val_data],dtype=tf.int32)
val_int_data_embed = tf.nn.embedding_lookup(norm_embedd,val_int_data)
similarity = tf.matmul(val_int_data_embed,tf.transpose(norm_embedd))

开始训练

with tf.Session() as sess:
    saver = tf.train.Saver()
    sess.run(tf.global_variables_initializer())
    writer = tf.summary.FileWriter('./graphs/embed',sess.graph)

    for epoch in range(epochs+1):
        total_loss = 0
        for x_batch,y_batch in get_batch(int_word,batch_size,window_size=window_size):

            _,tmp_loss = sess.run([optimizer,loss],feed_dict={x_input:x_batch,y_label:np.array(y_batch)[:, None]})

            total_loss += tmp_loss
        if epoch % 10 == 0:
            print('Epoch {}/{} train_loss {}'.format(epoch,epochs,total_loss))
        if epoch % 15 == 0:
            sim = similarity.eval()
            for i,j in enumerate(val_data):
                # 之所以sim取负号是因为为了从余弦距离最大到最小排列,因为argsort返回的是最小到达排列的索引
                nearest_n = (-sim[i,:]).argsort()[1:top_n+1]
                logg = 'Nearest to %s is :' % j
                for ind,ner_int_word in enumerate(nearest_n):
                    nearest_word = int_to_word[ner_int_word]
                    logg = '%s  %s'%(logg,nearest_word)
                print(logg)       
    save_path = saver.save(sess, "checkpoints/text8.ckpt")
    embed_mat = sess.run(norm_embedd)
    writer.close()
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Skip-gram是一种用于自然语言处理的模型,它的目的是根据中心词来预测周围的上下文。数据预处理是Skip-gram模型的重要步骤之一,下面将介绍Skip-gram模型的数据预处理过程Skip-gram模型的数据预处理过程如下: 1. 首先,需要读取语料库中的所有文本,并将它们转换为小写形式。 2. 接着,需要分词,将每个句子切分成单词或标点符号。这里可以使用分词工具如jieba等。 3. 然后,需要建立词表,将所有不同的单词或标点符号映射到一个唯一的整数值,这个整数值称为词汇表中单词的索引。 4. 接下来,需要将每个句子中的单词转换为对应的索引,以便后续的训练过程中可以直接使用索引来访问每个单词的向量表示。 5. 最后,需要将数据转换成Skip-gram模型所需的输入和输出格式。具体来说,输入是中心单词的索引,输出是上下文单词的索引列表。Skip-gram模型的输入是一个中心词,它会根据窗口大小选择上下文单词。 下面是一个简单的Python代码示例,用于实现Skip-gram模型的数据预处理过程: ```python import jieba # 读取语料库中的文本 with open('corpus.txt', 'r', encoding='utf-8') as f: corpus = f.read().lower() # 分词 sentences = [list(jieba.cut(line.strip())) for line in corpus.split('\n')] # 建立词表 vocab = {} for sentence in sentences: for word in sentence: if word not in vocab: vocab[word] = len(vocab) # 将单词转换为索引 indexed_sentences = [[vocab[word] for word in sentence] for sentence in sentences] # 将数据转换成Skip-gram模型所需的输入和输出格式 window_size = 2 X, y = [], [] for sentence in indexed_sentences: for i, word in enumerate(sentence): for j in range(max(0, i - window_size), min(len(sentence), i + window_size + 1)): if j != i: X.append(word) y.append(sentence[j]) ``` 以上是一个简单的Skip-gram模型的数据预处理过程示例,实际应用中可能需要进行更复杂的处理,以适应不同的任务和数据集。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值