Blog of YouSiki

High school student from China, trying to enter PekingUniversity.

【人工智能实践】用Tensorflow搭建神经网络

用Tensorflow搭建简单的神经网路(NN)之前需要先了解一点Tensorflow的知识。

基本步骤

  1. 用张量(Tensor)表示数据
  2. 用计算图(Graph)构建网络
  3. 用会话(Session)执行计算图并训练网络

Tensorflow的基本知识

张量

我对张量的理解就是——任意维度的数组(好像Python里叫做列表)。

可以是1 x 1的数组,也就是一个单独的数,叫做标量。

可以是1 x N的数组,就是一个一维数组,也就是向量。

可以是N x N的数组,那就是一个二维数组,此时就是矩阵了。

可以是N x N x N的数组,我也不知道该叫什么了。

可以用阶数来描述它,比如标量是0阶的,向量是1阶的,矩阵是2阶的。

数据类型

Tensorflow里,数据类型有tf.int32tf.float32等。

计算图

“搭建神经网络的计算过程,是承载一个或多个计算节点的一张图,只搭建网络,不运算。”

计算图用来描述各个变量之间的关系,表示出每个张量的计算方式,比如c = a + b

会话

构建完计算图之后,Tensorflow只是知道每个张量应如何计算,但并不会计算出张量的值。而在会话中,我们可以让Tensorflow计算出某个张量的值。


我们现在可以编写一个简单的Tensorflow程序,用来实践上面学到的内容。

# coding=utf-8

import tensorflow as tf

# add 两个1x2的向量相加

a = tf.constant([1.0, 2.0])
b = tf.constant([3.0, 4.0])
c = a + b

# mul 一个1x2的矩阵和一个2x1的矩阵相乘

x = tf.constant([[1.0, 2.0]])
y = tf.constant([[1.0], [2.0]])
z = tf.matmul(x, y)

# 此时只构建了计算图,并没有真的计算出c和z的值
# 如果输出c和z,只能知道它们的类型

print('c:', c) # c应当是一个1x2的向量
print('z:', z) # z应当是一个1x1的标量

# 如果想计算出c和z的值,就需要用到对话

with tf.Session() as sess:
    print('c:', sess.run(c))
    print('z:', sess.run(z))

上面这段代码的输出是

c: Tensor("add:0", shape=(2,), dtype=float32)
z: Tensor("MatMul:0", shape=(1, 1), dtype=float32)
c: [4. 6.]
z: [[5.]]

Tensorflow中常用的函数

函数 用处
tf.constant 直接按照给定的参数(数组)生成常量)
tf.Variable 按照给定的参数生成变量
tf.random_normal 生成正态分布随机数
tf.truncated_normal 生成去掉过大偏离点的正态分布随机数
tf.random_uniform 生成均匀分布随机数
tf.zeros 生成全0数组
tf.ones 生成全1数组
tf.fill 生成全为指定值的数组

举一些例子具体说明。

w = tf.Variable(tf.random_normal([2,3], stddev = 2, mean = 0, seed = 1)) 生成正态分布的随机数,是一个2x3的矩阵,标准差为2,均值为0,随机种子为1。若不加随机种子,每次的数据将不一样。后三个参数可以省略。

w = tf.Variable(tf.truncated_normal([2,3], stddev = 2, mean = 0, seed = 1)) 生成去掉偏离过大的数据后的正态分布随机数。如果随机出来的数据偏离平均值超过两个标准差,这个数据将重新生成。

w = random_uniform(shape = 7, minval = 0, maxval = 1, dtype = tf.int32, seed = 1) 表示从一个均匀分布[minval maxval)中随机采样。注意定义域是左闭右开,即包含minval,不包含maxval。

w = tf.zeros([3,2], int32) 生成的是[[0,0],[0,0],[0,0]]

神经网络的实现过程

  1. 准备数据集,提取特征,作为输入喂给神经网络(Neural Network,即NN)
  2. 搭建 NN 结构,从输入到输出,先搭建计算图,再用会话执行( NN 前向传播算法 -> 计算输出)
  3. 大量特征数据喂给 NN,迭代优化 NN 参数 ( NN 反向传播算法 -> 优化参数训练模型)
  4. 使用训练好的模型预测和分类

前向传播和反向传播

前向传播就是搭建模型的计算过程,让模型具有推理能力,可以针对一组输入给出相应的输出。

反向传播就是训练模型参数,在所有参数上用梯度下降,使 NN 模型在训练数据上的损失函数最小。

反向传播训练方法有梯度下降、momentum 优化器、adam 优化器等。

train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)
train_step = tf.train.MomentumOptimizer(learning_rate, momentum).minimize(loss)
train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)

learning_rate是学习率,决定每次参数更新的幅度。过大可能无法收敛,过小训练速度太慢。一般可以取0.01之类的数。

然后大概就可以照葫芦画瓢了——

比如用一个单隐藏层的神经网络来学习一下3x+4y这个函数。

import numpy as np
import tensorflow as tf

BATCH_SIZE = 256
TRAIN_STEPS = 2048
DATA_SIZE = 4096

X = np.random.randn(DATA_SIZE, 2)
Y = [[3.0 * a + 4.0 * b] for (a, b) in X]

x = tf.placeholder(tf.float32, shape = (None, 2))
y = tf.placeholder(tf.float32, shape = (None, 1))

w1 = tf.Variable(tf.random_normal([2, 8]))
w2 = tf.Variable(tf.random_normal([8, 1]))

h = tf.matmul(x, w1)
t = tf.matmul(h, w2)

init = tf.global_variables_initializer()
loss = tf.reduce_mean(tf.square(y - t))
step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)

with tf.Session() as sess:
    sess.run(init)
    for i in range(TRAIN_STEPS):
        st = (i * BATCH_SIZE) % DATA_SIZE
        ed = st + BATCH_SIZE
        sess.run(step, feed_dict = {x : X[st:ed], y : Y[st:ed]})
        if i % 256 == 0:
            print("loss", sess.run(loss, feed_dict = {x : X, y : Y}))

运行一下可以看到最后的Loss函数还是很小的,效果不错。