1. 深度学习
特性:
- 多层:单层神经网络其实就是感知机(1958年由Frank Rosenblatt提出),感知机的最大问题在于无法解决异或问题。
- 非线性:线性模型不管有多少层,其实和单层没有区别,本质还是线性模型,因为线性模型的最大特点是任意线性模型的组合任然是线性模型。节点的激活函数(在线性函数外面又包了一层的函数)决定了模型是否是线性的。常用的非线性模型有ReLU、sigmoid、tanh。
ReLU:
sigmoid:
tanh:

a = tf.nn.relu(tf.matmul(x, w1) + biases1)
y = tf.nn.relu(tf.matmul(a, w2) + biases2)
2. 经典损失函数
交叉熵
判断一个输出向量和期望的向量距离的一个常用方法是使用交叉熵。
交叉熵描述的是两个概率分布p、q之间的距离。
- 值越小,两个概率分布的距离越近。
- 交叉熵是不对称的,即
,前着表述的是通过概率分布q来表达p的困难程度。当用于神经网络的损失函数时,p代表正确值,q表示预测值。
# 二值输出的交叉熵样例
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))
+ (1 - y_) * tf.log(tf.clip_by_value(1 - y, 1e-10, 1.0)))
'''
y_是正确值, y是预测值。
tf.clip_by_value(y, 1e-10, 1.0) 将 y的值限定在1e-10, 1.0之间(大于1的统统为1,小于等于0的统统为1e-10)。保证不会出现log0的情况。
* 是各元素相乘,不是矩阵相乘。
tf.reduce_mean计算矩阵所有元素的平均值。
'''
但是神经网络的输出却不一定是一个概率分布,那么如何将神经网络向前传播得到的结果也变成概率分布呢?
Softmax回归
假设原始的输出是,经过softmax处理之后的输出为:
- 原始的输出作为新输出的置信度
- 新的输出满足概率分布的要求:
(1)任意事件发生的概率在0到1之间 。
(2)所有事件的概率之和为1。 - 新的输出可以理解成一个样例为不同类别的概率分别为多大。
# 使用了softmax回归之后的交叉损失函数
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)
# 针对只有一个正确值的加速版本
cross_entropy_for_only_one_class = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y_, logits=y)
均方误差
对于回归问题,一般只有一个输出节点,即预测值,最常用的损失函数是均方误差。
mse_loss = tf.reduce_mean(tf.square(y_ - y))
3. 神经网络优化算法
梯度下降算法
用于优化单个参数的取值。算法细节参考这篇简书
反向传播算法
给出了一个高效的方式,在所有参数上使用梯度下降算法。算法细节参阅这篇简书
论文Learning-Representations-by-back-propagating-errors
4. 神经网络算法优化
学习率
'''
指数衰减发
'''
import tensorflow as tf
global_step = tf.Variable(0)
learning_rate = tf.train.exponential_decay( #生成学习率
learning_rate=0.1, #初始学习率
global_step=global_step, #当前迭代次数
decay_steps=100, #衰减速度 (在迭代到该次数时学习率衰减为learning_rate * decay_rate)
decay_rate=0.96, #衰减系数
staircase=True, #True 表示 global_step/decay_steps会转化成整数,使得学习率的变化成一个阶梯函数,这样
name=None
)
'''
学习率的变化: decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
'''
train_op = tf.train.AdamOptimizer(learning_rate).minimize(any_loss, global_step=global_step)
'''
其他衰减方法:
tf.train.exponential_decay
tf.train.inverse_time_decay
tf.train.natural_exp_decay
tf.train.piecewise_constant
tf.train.polynomial_decay
'''
过拟合
解决过拟合的常用方法是正则化,正则化的细节请参考这篇简书
'''
均方误差+L2正则化的损失函数
'''
import tensorflow as tf
v_lambda = 0.001
w = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w)
mse_loss = tf.reduce_mean(tf.square(y_ - y) + tf.contrib.layers.l2_regularizer(v_lambda)(w))
实例
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
'''
1. 生成模拟数据集。
'''
dataset_size = 200
data = []
label = []
np.random.seed(0)
# 以原点为圆心,半径为1的圆把散点划分成红蓝两部分,并加入随机噪音。
for i in range(dataset_size):
x1 = np.random.uniform(-1,1)
x2 = np.random.uniform(0,2)
if x1**2 + x2**2 <= 1:
data.append([np.random.normal(x1, 0.1),np.random.normal(x2,0.1)])
label.append(0)
else:
data.append([np.random.normal(x1, 0.1), np.random.normal(x2, 0.1)])
label.append(1)
data = np.hstack(data).reshape(-1,2)
label = np.hstack(label).reshape(-1, 1)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label), # np.squeeze: 从数组的形状中删除单维度条目,即把shape中为1的维度去掉
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.show()

'''
2. 定义一个获取权重,并自动加入正则项到损失的函数。一般tf.Variable都是需要训练的权重参数
'''
def get_weight(shape, var_lambda):
w = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(var_lambda)(w))
return w
'''
3. 定义神经网络。
'''
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
# 每层节点的个数
layer_dimension = [2,10,5,3,1]
n_layers = len(layer_dimension)
cur_layer = x
in_dimension = layer_dimension[0]
# 循环生成网络结构,输入层X[None, 2],隐藏层W1[2, 10]、W2[10, 5]、W3[5, 3],输出层Y[None, 1]
for i in range(1, n_layers):
out_dimension = layer_dimension[i]
weight = get_weight([in_dimension, out_dimension], 0.003)
bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
cur_layer = tf.nn.elu(tf.matmul(cur_layer, weight) + bias)
in_dimension = layer_dimension[i]
y= cur_layer
# 损失函数的定义。
mse_loss = tf.reduce_sum(tf.pow(y_ - y, 2)) / dataset_size
tf.add_to_collection('losses', mse_loss)
loss = tf.add_n(tf.get_collection('losses'))
'''
4. 训练不带正则项的损失函数mse_loss。
'''
# 定义训练的目标函数mse_loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(mse_loss)
TRAINING_STEPS = 10000
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
sess.run(train_op, feed_dict={x: data, y_: label})
if i % 1000 == 1000 - 1:
print("After %d steps, mse_loss: %f" % (i,sess.run(mse_loss, feed_dict={x: data, y_: label})))
# 画出训练后的分割曲线
xx, yy = np.mgrid[-1.2:1.2:.01, -0.2:2.2:.01]
grid = np.c_[xx.ravel(), yy.ravel()]
probs = sess.run(y, feed_dict={x:grid})
probs = probs.reshape(xx.shape)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label),
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()

'''
5. 训练带正则项的损失函数loss
'''
# 定义训练的目标函数loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(loss)
TRAINING_STEPS = 10000
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
sess.run(train_op, feed_dict={x: data, y_: label})
if i % 1000 == 1000 - 1:
print("After %d steps, loss: %f" % (i, sess.run(loss, feed_dict={x: data, y_: label})))
# 画出训练后的分割曲线
xx, yy = np.mgrid[-1:1:.01, 0:2:.01]
grid = np.c_[xx.ravel(), yy.ravel()]
probs = sess.run(y, feed_dict={x:grid})
probs = probs.reshape(xx.shape)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label),
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()

滑动平均模型
v1 = tf.Variable(0, dtype=tf.float32)
step = tf.Variable(0, trainable=False)
ema = tf.train.ExponentialMovingAverage(decay=0.99, num_updates=step)
# 每一次操作的时候,列表变量[v1]都会被更新
maintain_averages_op = ema.apply([v1])
with tf.Session() as sess:
# 初始化
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run([v1, ema.average(v1)]))
# 更新变量v1的取值
sess.run(tf.assign(v1, 5))
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
# 更新step和v1的取值
sess.run(tf.assign(step, 10000))
sess.run(tf.assign(v1, 10))
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
# 更新一次v1的滑动平均值
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
网友评论