zoukankan      html  css  js  c++  java
  • word2vec

    http://www.360doc.com/content/17/0705/18/10408243_669124232.shtml
    # -*- coding:utf-8 -*-
    import math
    import os
    import random
    import zipfile
    import numpy as np
    import urllib
    import tensorflow as tf
    import collections

    from sklearn.manifold import TSNE
    import matplotlib.pyplot as plt


    url='http://mattmahoney.net/dc/'

    #下载数据
    def maybe_download(filename,expected_bytes):
    #文件是否存在
    if not os.path.exists(filename):
    #urlretrieve() 方法直接将远程数据下载到本地。
    filename,_=urllib.urlretrieve(url+filename,filename)

    #获取文件的状态
    statinfo=os.stat(filename)
    if statinfo.st_size==expected_bytes:
    print ('found and verified',filename)
    else:
    print (statinfo.st_size)
    raise Exception(
    'failed to verify '+filename+'. can you get to it with a browser?'
    )
    return filename

    #下载数据文件
    filename=maybe_download('text8.zip',31344016)

    #读取zip文件的内容
    def read_data(filename):
    with zipfile.ZipFile(filename) as f:
    data=tf.compat.as_str(f.read(f.namelist()[0])).split()
    return data


    #words list
    words=read_data(filename)
    print (type(words))
    print ('Data size',len(words))
    #1000多万个单词


    #单词库 5万
    vocabulary_size=50000

    def build_dataset(words):
    count=[['UNK',-1]]#list 此时,len(count)=1,表示只有一组数据 词汇表vocabulary

    count.extend(collections.Counter(words).most_common(vocabulary_size-1))
    #这句话,将words中,最常见的49999个单词和对应的个数,放入count中。
    ##此时,len(count)=50000,表示只有50000组数据
    #使用collections.Counter统计word单词列表中单词的频数,然后使用most_common方法取
    #top 50000频数的单词作为词汇表vocabulary

    dictionary=dict()#创建一个字典
    #将全部单词转为编号(以频数排序的编号),top50000之外的单词,认为UnKown,编号为0,并统计这类词汇的数量

    for word,_ in count:
    dictionary[word]=len(dictionary)#将全部单词转为编号(以频数排序的编号)
    #count中排序前五个为:the 1061396 of 593677 and 416629 one 411764 in 372201,......
    #那么在dictionary中,dictionary['the']=1 dictionary['of']=2
    #dictionary['and']=3 dictionary['one']=4,............
    #

    data=list()
    unk_count=0
    for word in words:#遍历单词列表,
    #对于其中每一个单词,先判断是否出现在dictionary中,
    if word in dictionary:
    #如果出现,则转为其编号
    index=dictionary[word]
    else:#如果不是,则转为编号0
    index=0
    unk_count+=1
    data.append(index)
    #data是dictionary的数值 逆序 (频数)
    #reverse_dictionary是dictionary的整个逆序
    # 通过data索引获取的值,便可以通过reverse_dictionary查看单词

    count[0][1]=unk_count
    #count[0]:表示第一个的键,


    reverse_dictionary=dict(zip(dictionary.values(),dictionary.keys()))

    #最后返回转换后的编码data,通过data[索引],就可以得到一个数值,通过该数值,在reverse_dictionary就能知道该数值代表的单词
    #例如:data[1]=3084 reverse_dictionary[3084]='originated'
    # 每个单词的频数统计count,词汇表,词汇表反转的形式
    return data,count,dictionary,reverse_dictionary
    #dictionary和reverse_dictionary是互为对应的。
    #dictionary 键‘字符串’ 值 整数数值 int类型
    #reverse_dictionary 键 整数数值 int类型 值‘字符串’

    #将从text解压出来的1000多万个单词,进行数据处理
    #删除原始单词列表,以节约内存。
    data,count,dictionary,reverse_dictionary=build_dataset(words)


    del words
    print ('most common words (+UNK)',count[:5])#打印词汇表count,中,出现频率最高的5个。
    print ('sample data',data[:10],[reverse_dictionary[i] for i in data[:10]])
    #编码data中前10个单词----编号----


    data_index=0
    #生成word2vec的训练用的batch数据,参数中batch_size为batch的大小;
    #skip_window指单词最远可以联系的距离,设为1表示只能跟紧邻的两个单词生成样本。
    #num_skips为对每个单词生成多少个样本,不能大于skip_windows的两倍,并且batch_size是其整数倍
    def generate_batch(batch_size,num_skips,skip_window):

    global data_index #数据索引,全局变量
    #确保batch_size可以整除num_skips
    assert batch_size%num_skips==0
    assert num_skips<=2*skip_window#
    #python assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。
    # 可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常。

    batch=np.ndarray(shape=(batch_size),dtype=np.int32)
    #创建一个batch_size大小的数组,数据类型为int32类型,数值随机, 从0到2的32次方
    labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)#数据维度:[batch_size,1]

    span = 2 * skip_window + 1 # [ skip_window target skip_window ] 跨度
    #span为对某个单词创建相关样本时,会是严格哦你到的单词数量。包括目标单词本身和它前后的单词

    buffer = collections.deque(maxlen=span) # buffer用来存取w上下文word的id
    #创建最大容量为span的deque,双向队列。

    for _ in range(span):
    buffer.append(data[data_index]) # data:ids 在对双向队列deque使用append方法添加变量时,只会保留最后插入的span个变量
    data_index = (data_index + 1) % len(data)
    #从序号data_index开始,把span个单词顺序读入buffer作为初始值。因为容量为span的deque,所以此时buffer已经填充满
    #后续数据会替换掉前面的数据。

    #进入到第以层循环,次数为 batch_size // num_skips
    for i in range(batch_size // num_skips): # how many num_skips in a batch
    #每次循环内,对一个目标单词生成样本。

    #现在bufffer中目是标单词和所有相关单词。定义target = skip_window ,
    #即buffer中第skip_window个变量为目标单词
    #然后定义生成样本时需要避免的单词列表targets_to_avoid,该列表一开始就包括第skip_window个单词(目标单词)
    #因为要预测的是语境单词,不包括单词本身。

    target = skip_window # target label at the center of the buffer
    targets_to_avoid = [skip_window] # extract the middle word

    #第二层循环
    for j in range(num_skips):
    #每次循环中对一个语境单词单词生成样本。#先产生随机数,直到随机数不再targets_to_avoid中。
    #代表可以使用的语境单词,然后产生一个样本。
    while target in targets_to_avoid: #直到随机数不再targets_to_avoid中。 context中的word,一个只取一次
    target = random.randint(0, span - 1)#先产生随机数,

    targets_to_avoid.append(target) ##代表可以使用的语境单词,然后产生一个样本。

    batch[i * num_skips + j] = buffer[skip_window] #buffer[skip_window]目标词汇 feature
    labels[i * num_skips + j, 0] = buffer[target] # label 标签:buffer[target]
    #同时,因为这个语境单词被使用了,所以在把它添加到targets_to_avoid中过滤

    #在对一个目标单词生成完所有的样本之后(num_skip个样本),我们在读下一个单词,同时会抛掉buffer中的第一个单词,即
    #把滑窗向后移以为,这样目标单词也向后移动以为,语境单词也整体后移了,便可以生成下一个目标单词的训练样本。
    buffer.append(data[data_index]) # update the buffer, append the next word to buffer
    data_index = (data_index + 1) % len(data)

    #两层循环完成之后,就已经获得batch_size个训练样本,将batch和labels作为函数结果返回。
    return batch, labels # batch: ids [batch_size] lebels:ids [batch_size*1]


    #调用generate_batch简单测试功能。
    batch, labels = generate_batch(batch_size=8, num_skips=2, skip_window=1)

    for i in range(8):
    print(batch[i], reverse_dictionary[batch[i]],'->', labels[i, 0], reverse_dictionary[labels[i, 0]])



    #在本代码中,训练时,batch_size代表的是一个batch中,word的个数,而不是sentense的个数。
    batch_size = 128
    embedding_size = 128 # 单词转为稠密向量的维度,这里使用128作为词向量的维度
    skip_window = 1 # How many words to consider left and right.单词间最远可以联系的距离
    num_skips = 2 # 每个目标单词提取的样本数How many times to reuse an input(buffer) to generate a label.

    #验证数据 valid_examples,这里随机抽取一些频数最高的单词,看向量空间上跟他们最近的单词是否相关性比较高
    valid_size = 16 # 抽取的验证单词数
    valid_window = 100 # 指验证单词只从频数最高的100个单词中抽取
    valid_examples = np.random.choice(valid_window, valid_size, replace=False)#使用np.random.choice函数进行随机抽取
    num_sampled = 64 # 训练是用作负样本的噪声单词的数量


    #
    graph = tf.Graph()
    with graph.as_default():

    # 创建训练数据中的inputs和balels的placeholder
    #在这里,我们只输入word对应的id,假设batch_size是128,那么我们第一次就输入文本前128个word所对应的id
    train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
    #labels和inputs是一样的, 只不过一个是行向量(tensor),一个是列向量(tensor)
    train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])

    valid_dataset = tf.constant(valid_examples, dtype=tf.int32)#
    #将前面随机产生的valid_examples转换为Tensorflow中的constant

    # Ops and variables pinned to the CPU because of missing GPU implementation
    with tf.device('/cpu:0'):
    # Look up embeddings for inputs.
    #使用tf.random_uniform随机生成所有单词的词向量embeddings
    #单词表大小为50000,向量维度128
    embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
    #tf.nn.embedding_lookup查找输入的train_inputs对应的向量embed.
    embed = tf.nn.embedding_lookup(embeddings, train_inputs)

    # Construct the variables for the NCE loss
    #权重参数
    nce_weights = tf.Variable( #every word has a corresponding nce_weight ad nce_biase
    tf.truncated_normal([vocabulary_size, embedding_size],
    stddev=1.0 / math.sqrt(embedding_size)))
    #偏置
    nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

    # Compute the average NCE loss for the batch.
    # tf.nce_loss automatically draws a new sample of the negative labels each
    # time we evaluate the loss.
    #使用tf.nn.nce_loss计算学习出的词向量embedding在训练数据上的loss,并使用tf.reduce_mean进行汇总
    loss = tf.reduce_mean(
    tf.nn.nce_loss(weights=nce_weights, biases=nce_biases, inputs=embed, labels=train_labels,
    num_sampled=num_sampled, num_classes=vocabulary_size))#关于nce_loss的介绍在文章最后

    # Construct the SGD optimizer using a learning rate of 1.0.
    #优化器为SGD,学习素来为1.0
    optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)

    # Compute the cosine similarity between minibatch examples and all embeddings.
    #计算嵌入向量embeeddings的L2范数norm
    norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))
    #再将embeeddings除以其L2范数得到标准化的normalized_embeddings
    normalized_embeddings = embeddings / norm
    #再使用tf.nn.embedding_lookup查询验证单词的嵌入向量,并
    valid_embeddings = tf.nn.embedding_lookup(
    normalized_embeddings, valid_dataset)
    #并计算验证单词的嵌入向量和词汇表中所有单词的相似性
    similarity = tf.matmul(
    valid_embeddings, normalized_embeddings, transpose_b=True)

    # Add variable initializer.初始化所有模型参数
    init = tf.global_variables_initializer()

    # Step 5: Begin training.
    num_steps = 100001

    temp=tf.Variable(tf.random_normal(shape=[6]))
    # 保存操作


    with tf.Session(graph=graph) as session:
    # We must initialize all variables before we use them.
    init.run()
    print("Initialized")
    summary_writer = tf.summary.FileWriter('./Out', session.graph)
    saver = tf.train.Saver(max_to_keep=5)

    average_loss = 0
    for step in xrange(num_steps):
    batch_inputs, batch_labels = generate_batch(batch_size, num_skips, skip_window)
    feed_dict = {train_inputs : batch_inputs, train_labels : batch_labels}

    # 使用session.run执行一次优化器运算(即一次参数更新)和损失计算
    _, loss_val = session.run([optimizer, loss], feed_dict=feed_dict)
    average_loss += loss_val

    #每2000次循环,计算平均loss并显示出来
    if step % 2000 == 0:
    if step > 0:
    average_loss /= 2000
    # The average loss is an estimate of the loss over the last 2000 batches.
    print("Average loss at step ", step, ": ", average_loss)
    average_loss = 0
    saver.save(session, "word2vec", global_step=step)


    # Note that this is expensive (~20% slowdown if computed every 500 steps)
    #每10000次循环,计算一次验证单词与全部单词的相似度,并将与每个验证单词最相似的8个单词展示出来
    if step % 10000 == 0:
    sim = similarity.eval()
    for i in xrange(valid_size):
    valid_word = reverse_dictionary[valid_examples[i]]
    top_k = 8 # number of nearest neighbors
    nearest = (-sim[i, :]).argsort()[1:top_k+1]
    log_str = "Nearest to %s:" % valid_word
    for k in xrange(top_k):
    close_word = reverse_dictionary[nearest[k]]
    log_str = "%s %s," % (log_str, close_word)
    print(log_str)
    final_embeddings = normalized_embeddings.eval()


    # Step 6: Visualize the embeddings.
    #定义一个用来可视化word2vec效果的函数。
    #low_dim_embs是降维到2维的单词的空间向量,我们将在图片中显示每个单词的位置

    def plot_with_labels(low_dim_embs, labels, filename='tsne.png'):
    assert low_dim_embs.shape[0] >= len(labels), "More labels than embeddings"
    plt.figure(figsize=(18, 18)) # in inches
    for i, label in enumerate(labels):
    x, y = low_dim_embs[i, :]
    plt.scatter(x, y)
    #展示单词本身
    plt.annotate(label,
    xy=(x, y),
    xytext=(5, 2),
    textcoords='offset points',
    ha='right',
    va='bottom')
    #保存文件
    plt.savefig(filename)


    try:
    from sklearn.manifold import TSNE
    import matplotlib.pyplot as plt

    #TSNE实现降维
    tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
    plot_only = 500
    #直接将原始的128维的嵌入向量降到2维,再用前面的plot_with_labels进行显示
    low_dim_embs = tsne.fit_transform(final_embeddings[:plot_only, :])
    labels = [reverse_dictionary[i] for i in xrange(plot_only)]
    plot_with_labels(low_dim_embs, labels)

    except ImportError:
    print("Please install sklearn, matplotlib, and scipy to visualize embeddings.")
    萍水相逢逢萍水,浮萍之水水浮萍!
  • 相关阅读:
    性能测试流程各阶段的工作
    利用jquery插件在客户端计算“过了多少时间”
    服务器×××上的MSDTC不可用解决办法
    SignalR server push 利器
    win8下vs2012中TFS更换用户的问题
    在Share Point 2010 中针对相应用户赋某一个list中的item相关权限
    .NET C#教程初级篇 11 基本数据类型及其存储方式
    新手日记
    shell脚本格式化
    干掉 Postman?测试接口直接生成API文档,ApiPost真香!
  • 原文地址:https://www.cnblogs.com/AIBigTruth/p/10334442.html
Copyright © 2011-2022 走看看