Stanford CS224n 第七讲:TensorFlow入门

1.TensorFlow介绍

  • TensorFlow计算模型——计算图
  • TensorFlow数据模型——张量
  • TensorFlow运行模型——会话
  • TensorFlow的训练过程

2.TensorFlow实现样例

  • TensorFlow实现线性回归demo
  • TensorFlow实现word2vec (Skip-gram)demo

深度学习框架也就像tensorflow、caffe、Keras等这些是深度学习的工具,简单来说就是库,编程时需要import 。打个比方,一套深度学习框架就是这个品牌的一套积木,各个组件就是某个模型或算法的一部分,你可以自己设计如何使用积木去堆砌符合你数据集的积木。好处是你不必重复造轮子。

1.TensorFlow介绍

  • TensorFlow计算模型——计算图

TensorFlow的名字已经说明了它最重要的两个概念——Tensor 和 Flow。如果说TensorFlow的第一个单词Tensor表明了它的数据结构,那么Flow则体现了它的计算模型。Flow翻译成中文就是“流”,它直观地表达了张量之间通过计算相互转换的过程。
在这里插入图片描述
TensorFlow是一个通过计算图的形式来表述计算的编程系统。TensorFlow中的每一个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。

  • TensorFlow数据模型——张量
    在这里插入图片描述
    TensorFlow程序一般可以分为两个阶段。在第一个阶段需要定义图中所有的计算,我们将在此部分以上图为例介绍;第二个阶段是执行计算,我们将在下一部分1.3中介绍。
    以上图为例,有以下几个节点定义:
    变量(Variables):其中W和b为变量。课程中一直强调变量就是我们模型中要训练的一些参数。变量保留多次执行过程中(训练)的当前值,可以存储,方便模型的迁移和复现。
    在TensorFlow中代码实现如下:
    import tensorflow as tf
    b=tf.Variable(tf.zeros((100,)))#定义全0的变量
    W=tf.Variable(tf.random_uniform((784,100),-1,1))#服从均匀分布

占位符(Placeholders):其中X是为占位符。占位符是在执行时才会接受值的节点,比如说神经网络中的输入,这种不依赖任何其他节点。对于占位符我们不初始化任何值,我们只定义它的数据类型和张量大小。
在TensorFlow中代码实现如下:

    import tensorflow as tf
    X=tf.placeholder(tf.float32,(100,784))

数学操作(Mathematical Operations):其中MatMul、Add和ReLU为数学操作节点。其实就是定义一些数学操作来操作其他节点。
在TensorFlow中代码实现如下:

    import tensorflow as tf
    h=tf.nn.relu(tf.multiply(x,W)+b)
  • TensorFlow运行模型——会话
    到现在为止,我们实际上并没有操作任何数据,只是介绍了TensorFlow是如何组织数据和运算的,而并没有任何数据在我们的计算图中流动。此部分介绍如何使用TensorFlow中的会话session来执行定义好的运算,会话拥有并管理TensorFlow程序运行时的所有资源。仍然以此博客中的例子为例,session实现代码如下:
	sess=tf.Session()#定义Session变量
	sess.run(tf.initialize_all_variables())#初始化所有变量
	sess.run(h,{x:np.random.rand(100,784)})#输入x是100*784的二维数组
  • TensorFlow的训练过程
    我们先是通过变量和占位符定义了计算图;然后我们给计算图定义了session会话,也就是运行环境;接下来我们就来看看如何训练模型代码过程:
	prediction=tf.nn.softmax(...)#神经网络的输出
	label=tf.placeholder(tf.float32,[100,10])#100是样本数,10是类别数
	cross_entropy=-tf.reduce_sum(label*tf.log(prediction),axis=1)#定义损失
	train_step=tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)#计算梯度和优化操作

2.TensorFlow实现样例

  • TensorFlow实现线性回归demo

根据课程中实现了TensorFlow版的线性回归(Github

import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt

#产生训练数据
def create_data():
    #y=x*2+0.3*random(符合正态分布的采样)
    x_batch=np.linspace(-1,1,100)#将-1到1之间分成100段,组成输入数组
    y_batch=2*x_batch+np.random.randn(*x_batch.shape)*0.3#根据x产生y的真实值
    return x_batch,y_batch

#定义次模型的计算图
def liner_regression():
    x=tf.placeholder(tf.float32,shape=(None,),name='x')#定义输入
    y=tf.placeholder(tf.float32,shape=(None,),name='y')#定义实际输出
    w=tf.Variable(np.random.normal(),name='w')#定义需训练的权重参数
    y_pred=tf.multiply(w,x)#定义预测输出
    loss=tf.reduce_mean(tf.square(y-y_pred))#计算loss
    return x,y,y_pred,loss

#模型运行函数session
def run():
    x_batch,y_batch=create_data()#返回输入x和真实的y值
    x, y, y_pred, loss=liner_regression()#得到计算图中的节点
    optimizer=tf.train.GradientDescentOptimizer(0.1).minimize(loss)#定义优化器
    init=tf.global_variables_initializer()#定义初始化参数操作
    with tf.Session() as session:#采用with定义session,因此会自动关闭session
        session.run(init)#初始化参数
        feed_dict={x:x_batch,y:y_batch}#定义计算图的输入字典
        for _ in range(50):#迭代次数
            loss_val=session.run(loss,feed_dict)#计算loss
            session.run(optimizer,feed_dict)#更新参数
            # loss_val,_=session.run([loss,optimizer],feed_dict)#也可以同时计算loss,更新参数
            print(loss_val)
        y_pred_batch=session.run(y_pred,feed_dict)#通过输入数据字典,获取计算图中计算出预测值
    plt.figure('liner_regression效果图')
    plt.scatter(x_batch,y_batch)#画散点图
    plt.plot(x_batch,y_pred_batch)#画连续图
    plt.show()

if __name__=='__main__':
    run()
  • TensorFlow实现word2vec (Skip-gram)demo

将TensorFlow实现word2vec的basic版过了一遍,写了详细的备注,并实现了一个小例子(Github)。
语料采用的金庸大师的《倚天屠龙记》《停用词表》采用的哈工大发布的语料。
效果和代码如下:
由于文本有限,效果肯定也就有限啦~
在这里插入图片描述

from numpy import random
import numpy as np
import collections
import math
import tensorflow as tf
import jieba
from collections import Counter


# 此函数作用是对初始语料进行分词处理
def cut_txt(old_file):
    cut_file = old_file + '_cut.txt' # 分词之后保存的文件名
    fi = open(old_file, 'r', encoding='utf-8') #注意操作前要把文件转化成utf-8文件
    text = fi.read()  # 获取文本内容
    new_text = jieba.cut(text, cut_all=False)  # 采用精确模式切词
    str_out = ' '.join(new_text)

    #去除停用词
    stopwords = [line.strip() for line in open('DataSet/中文停用词.txt', 'r',encoding='utf-8').readlines()]
    for stopword in stopwords:
        str_out=str_out.replace(' '+stopword+' ',' ')
    fo = open(cut_file, 'w', encoding='utf-8')
    fo.write(str_out)
    ret_list=str_out.split()#训练语料
    ret_set=list(set(ret_list))#字典
    list_len=len(ret_list)
    set_len=len(set(ret_list))
    print('总字数 总词数 :')
    print(list_len,set_len) #总字数 总词数
    print('词频字典 :')
    print(dict(Counter(ret_list)))# 词频字典
    return ret_list,ret_set

# 预处理切词后的数据
def build_dataset(words, n_words):
    count = [['UNK', -1]] #存放词频做大的n_words个单词,第一个元素为单词,第二个元素为词频。UNK为其他单词
    count.extend(collections.Counter(words).most_common(n_words - 1))#获取词频做大的n_words-1个单词(因为有了UNK,所以少一个)
    dictionary = dict() #建立单词的索引字典
    for word, _ in count:
        dictionary[word] = len(dictionary) #建立单词的索引字典,key为单词,value为索引值
    data = list()# 建立存放训练语料对应索引号的list,也就是说将训练的语料换成每个单词对应的索引
    unk_count = 0 #计数UNK的个数
    for word in words:
        if word in dictionary:
            index = dictionary[word]#获取单词在字典中的索引
        else:
            index = 0 #字典中不存在的单词(UNK),在字典中的索引是0
            unk_count += 1 #每次遇到字典中不存在的单词 UNK计数器加1
        data.append(index)#将训练语料对应的索引号存入data中
    count[0][1] = unk_count
    reversed_dictionary = dict(zip(dictionary.values(), dictionary.keys()))#将单词的索引字典反转,即将key和value对换
    return data, count, dictionary, reversed_dictionary#训练语料的索引;top大的单词词频;字典{单词:索引值};字典{索引值:单词}


# 为 skip-gram model 产生bathch训练样本.
#从文本总体的第skip_window+1个单词开始,每个单词依次作为输入,它的输出可以是上下文范围内的单词中的任何一个单词。一般不是取全部而是随机取其中的几组,以增加随机性。
def generate_batch(batch_size, num_skips, skip_window):#batch_size 就是每次训练用多少数据,skip_window是确定取一个词周边多远的词来训练,num_skips是对于一个输入数据,随机取其窗口内几个单词作为上下文(即输出标签)。
  global data_index
  assert batch_size % num_skips == 0#保证batch_size是 num_skips的整倍数,控制下面的循环次数
  assert num_skips <= 2 * skip_window #保证num_skips不会超过当前输入的的上下文的总个数
  batch = np.ndarray(shape=(batch_size), dtype=np.int32) #存储训练语料中心词的索引
  labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)#存储训练语料中心词对应的上下文的索引
  span = 2 * skip_window + 1  # [ skip_window target skip_window ]
  #这个很重要,最大长度是span,后面如果数据超过这个长度,前面的会被挤掉,这样使得buffer里面永远是data_index周围的span歌数据,
  #而buffer的第skip_window个数据永远是当前处理循环里的输入数据
  buffer = collections.deque(maxlen=span)#一个完整的窗口存储器
  if data_index + span > len(data):
    data_index = 0
  buffer.extend(data[data_index:data_index + span])
  data_index += span #获取下一个要进入队列的训练数据的索引
  for i in range(batch_size // num_skips):#一个batch一共需要batch个训练单词对,每个span中随机选取num_skips个单词对,所以要循环batch_size // num_skips次
    target = skip_window  # 中心词索引在buffer中的位置
    targets_to_avoid = [skip_window] #自己肯定要排除掉,不能自己作为自己的上下文
    for j in range(num_skips):#采样num_skips次
      while target in targets_to_avoid:
        target = random.randint(0, span - 1) #随机取一个,增强随机性,减少训练时进入局部最优解
      targets_to_avoid.append(target)#采样到某一个上下文单词后,下一次将不会再采样
      batch[i * num_skips + j] = buffer[skip_window] #这里保存的是训练的输入序列
      labels[i * num_skips + j, 0] = buffer[target] #这里保存的是训练时的输出序列,也就是标签
    if data_index == len(data): #超长时回到开始
        buffer.extend(data[0:span])
        data_index = span
    else:
        buffer.append(data[data_index]) #append时会把queue的开始的一个挤掉
        data_index += 1 #此处是控制模型窗口是一步步往后移动的
  data_index = (data_index + len(data) - span) % len(data)# 倒回一个span,防止遗漏最后的一些单词
  return batch, labels#返回 输入序列  输出序列


############ 第一步:对初始语料进行分词处理 ############
train_data,dict_data=cut_txt('DataSet/倚天屠龙记.txt')#切词
vocabulary_size =10000#字典的大小,只取词频top10000的单词

############ 第二步:预处理切词后的数据 ############
data, count, dictionary, reverse_dictionary = build_dataset(train_data,vocabulary_size)#预处理数据
print()
print(data)
print()
print(count)
print()
print(dictionary)
print()
print(reverse_dictionary)
print('Most common words (+UNK)', count[:5])#词频最高的前5个单词
print('Sample data', data[:10], [reverse_dictionary[i] for i in data[:10]])#前10个训练数据索引及其具体单词

############ 第三步:为 skip-gram model 产生bathch训练样本. ############
data_index = 0#控制窗口滑动的
batch, labels = generate_batch(batch_size=128, num_skips=8, skip_window=5)#产生一个batch的训练数据。batch大小128;从上下文中随机抽取8个单词作为输出标签;窗口大小5(即一个窗口下11个单词,1个人中心词,10个上下文单词);
for i in range(10):#输出一下一个batch中的前10个训练数据对(即10个训练样本)
    print(batch[i], reverse_dictionary[batch[i]],'->', labels[i, 0], reverse_dictionary[labels[i, 0]])

############ 第四步: 构造一个训练skip-gram 的模型 ############
batch_size = 128 #一次更新参数所需的单词对
embedding_size = 128  # 训练后词向量的维度
skip_window = 5  #窗口的大小
num_skips = 8  # 一个完整的窗口(span)下,随机取num_skips个单词对(训练样本)

# 构造验证集的超参数
valid_size = 16  # 随机选取valid_size个单词,并计算与其最相似的单词
valid_window = 100  # 从词频最大的valid_window个单词中选取valid_size个单词
valid_examples = np.random.choice(valid_window, valid_size, replace=False)#选取验证集的单词索引
num_sampled = 64  #负采样的数目

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

    train_inputs = tf.placeholder(tf.int32, shape=[batch_size])  #中心词
    train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1]) #上下文
    valid_dataset = tf.constant(valid_examples, dtype=tf.int32) #验证集

    embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))#定义单词的embedding
    embed = tf.nn.embedding_lookup(embeddings, train_inputs)#窗口查询中心词对应的embedding

    # 为 NCE loss构造变量
    nce_weights = tf.Variable(tf.truncated_normal([vocabulary_size, embedding_size],stddev=1.0 / math.sqrt(embedding_size)))#权重
    nce_biases = tf.Variable(tf.zeros([vocabulary_size]))#偏差

    # 对于一个batch,计算其平均的 NEC loss
    # 采用负采样优化训练过程
    loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weights,biases=nce_biases,labels=train_labels,inputs=embed,num_sampled=num_sampled,num_classes=vocabulary_size))

    #采用随机梯度下降优化损失函数,学习率采用1.0
    optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)

    # 从字典中所有的单词计算一次与验证集最相似(余弦相似度判断标准)的单词
    norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))#计算模
    normalized_embeddings = embeddings / norm #向量除以其模大小,变成单位向量
    valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset)#选出验证集的单位向量
    similarity = tf.matmul(valid_embeddings, normalized_embeddings, transpose_b=True)#验证集的单位向量,乘以所有单词的单位向量。得到余弦相似度

    # 变量初始化
    init = tf.global_variables_initializer()

############ 第五步:开始训练 ############
num_steps = 100001 #迭代次数
with tf.Session(graph=graph) as session:
    init.run()
    print('开始训练')

    average_loss = 0
    for step in range(num_steps):
        batch_inputs, batch_labels = generate_batch(batch_size, num_skips, skip_window)#产生一个batch
        feed_dict = {train_inputs: batch_inputs, train_labels: batch_labels}#tensor的输入


        _, loss_val = session.run([optimizer, loss], feed_dict=feed_dict)#得到一个batch的损失值
        average_loss += loss_val #损失值累加

        if step % 2000 == 0:#每迭代2000次,就计算一次平均损失,并输出
            if step > 0:
                average_loss /= 2000
            print('Average loss at step ', step, ': ', average_loss)
            average_loss = 0 #每2000次迭代后,将累加的损失值归零

        if step % 10000 == 0:#每迭代10000次 就计算一次与验证集最相似的单词,由于计算量很大,所以尽量少计算相似度
            sim = similarity.eval()
            for i in range(valid_size):
                valid_word = reverse_dictionary[valid_examples[i]]#得到需验证的单词
                top_k = 10  # 和验证集最相似的top_k个单词
                nearest = (-sim[i, :]).argsort()[1:top_k + 1]#最邻近的单词的索引,[1:top_k + 1]从1开始,是跳过了本身
                log_str = 'Nearest to %s:' % valid_word
                for k in range(top_k):
                    close_word = reverse_dictionary[nearest[k]]#获得第k个最近的单词
                    log_str = '%s %s,' % (log_str, close_word) #拼接要输出的字符串
                print(log_str)
    final_embeddings = normalized_embeddings.eval()
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值