<jupyter_start><jupyter_text>Tensorflow 实战 Google 深度学习框架 | 学习笔记（二）>Shoot on the moon and if you miss you will still be among the stars.Caicloud Github :[tensorflow-tutorial](https://github.com/caicloud/tensorflow-tutorial)https://github.com/caicloud/tensorflow-tutorial原 tutorial 使用的 Tensorflow 最新版本是 1.4.0 ，本人使用的 1.5.0 版本，所以部分代码会略有不同，该笔记仅为个人学习，理解使用。如有错误，还望批评指教。----ZJ 4 深层神经网络 4.1 深度学习与深层神经网络wiki 深度学习定义：一类通过多层非线性变换对高复杂性数据建模算法的合集。深度学习，两个重要特性：**多层 and 非线性。**- 线性模型的局限性，线性模型能够解决的问题是有限的(解决线性可分的问题)。    - 3.4.2 小节中，输出为所有输入的加权和，导致整个神经网络是一个线性模型。    - （采用线性函数）任意层的全连接神经网络（FC）和单层神经网络模型的表达能力没有任何区别。    - 线性模型可以解决线性可分的问题，如通过直线（或高维空间的平面）划分。    - 现实世界中，绝大多数问题是无法线性分割的，所以出现 Relu 这样非线性的激活函数，来解决高复杂性的问题（深度学习的目的就是解决这样的问题）    - 激活函数实现去线性化    - 如果每个神经元的输出通过一个非线性函数，那么整个神经网络就不再是线性的了。    - 添加偏置项（bias）,偏置项 是神经网络中非常有用的一种结构。    - 每个节点（神经元）的输出在 加权和的基础上，还做了一个非线性变换。    - 常用非线性激活函数（tf.nn.relu, tf.sigmoid, tf.tanh）- 多层网络解决异或运算    - 在神经网络的发展史，有个很重要的问题，**异或问题**。    - 1958 感知机模型，将输入进行加权和，没有隐藏层，然后经过激活函数最后得到输出。不能解决异或问题。    - 异或问题（直观理解）：两个输入的符号相同时（同正，同负）输出为 0 ，否则（一正一负）输出为 1.    - 加入隐藏层后，异或问题得到很好的解决    - 深层网络有组合特征提取的功能，这个特性对于解决不易提取特征向量的问题（如图片识别，语音识别等）有很大的帮助。 4.2 损失函数的定义**神经网络模型的效果以及优化的目标是通过损失函数（loss function）来定义的。**监督学习的两大种类：分类问题 and 回归问题。- 经典损失函数- 自定义损失函数如何判断一个输出向量和期望的向量有多接近？------交叉熵（cross entropy）是最常用的评判方法之一。交叉熵 （cross entropy）:给定两个概率分布 p,q $$H(p,q)= - \sum_{x}p(x)logq(x)$$注意：交叉熵刻画的是两个概率之间的分布，但是神经网络的输出不一定是一个概率分布。- 概率分布刻画了不同事件发生的概率，当事件总数有限的情况下，概率分布函数 $p(X=x)$ 满足：$$\forall x \space\space p(X=x)\in[0,1]  且 \sum_{x}p(X=x)=1$$- 任意事件发生的概率都在 0 和 1 之间- 且总有某一个事件发生（概率的和为 1）如何将神经网络前向传播的结果变成概率分布？- softmax 回归是一个常用的方法【图】4_10假设原始的神经网络输出为 $y_1,y_2,\dots,y_n$,那么经过 softmax 处理之后的输出为：$$ softmax(y)_i=y_i^{'}=\frac{e^{yi}}{\sum_{j=1}^{n}e^{yj}}$$- 原始神经网络的输出被用作置信度来生成新的输出,新的输出满足概率分布的所有要求- 新的输出可以理解为经过神经网络的推导，一个样本为不同类别的概率分别有多大- 因此神经网络的输出变为了一个概率分布，从而可以通过交叉熵来计算预测概率分布和真实答案的概率分布之间的距离。- 交叉熵函数不是对称的 （$H(p.q)\neq H(q,p)$）,刻画的是通过概率 q 来表达概率分布 p 的困难程度- 在神经网络中，p 代表正确标签，q 代表预测值<jupyter_code>import tensorflow as tf

tf.__version__
'''
Tensorflow 实现交叉熵

'''

cross_entropy = -tf.reduce_mean(y_* tf.log(tf.clip_by_value(y, 1e-10, 1.0)))<jupyter_output><empty_output><jupyter_text>reduce_mean 可以理解为 $ \frac{1}{n} \sum$ ,n 代表一个 batch 中样例的数量其中：`y_ `代表正确结果， `y` 代表预测值上面一行代码，包含了 4 种不同的 Tensorflow 运算。- `tf.clip_by_value`函数可以将在一个张量中的数值限制在一个范围之内，可以避免一些运算错误，（如 log0 是无效的）   - 小于 2.5 的数值替换为 2.5 ，大于 4.5 的数值 替换为 4.5  - 通过 `tf.clip_by_value`  函数可以保证在进行 log 运算时，不会出现 log0 这样的错误<jupyter_code>import tensorflow as tf

v = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])

with tf.Session() as sess:
    print(tf.clip_by_value(v, 2.5, 4.5).eval())<jupyter_output>[[2.5 2.5 3. ]
 [4.  4.5 4.5]]<jupyter_text>- 第二个运算是 `tf.log` 函数，完成对张量中所有函数一次求对数的功能<jupyter_code>v = tf.constant([1.0, 2.0, 3.0])

with tf.Session() as sess:
    
    print(tf.log(v).eval())<jupyter_output>[0.        0.6931472 1.0986123]<jupyter_text>- 第三个是乘法运算，在实现交叉熵的代码中直接将两个矩阵通过 `*`操作相乘，这个操作不是矩阵乘法，而是元素之间直接相乘 （element-wise）- 矩阵乘法需要使用 `tf.matmul` 函数完成。下面示例给出两者区别：<jupyter_code>v1 = tf.constant([[1.0, 2.0], [3.0, 4.0]])
v2 = tf.constant([[5.0, 6.0], [7.0, 8.0]])

with tf.Session() as sess:
    print('逐元乘积：\n',(v1*v2).eval())
    print('矩阵乘法:\n', tf.matmul(v1, v2).eval())<jupyter_output>逐元乘积：
 [[ 5. 12.]
 [21. 32.]]
矩阵乘法:
 [[19. 22.]
 [43. 50.]]<jupyter_text>- `v1 * v2` 的结果是每个位置上对应元素的乘积，如结果中（1,1）元素值 5 是 `1×5 = 5`- `tf.matmul` 函数完成矩阵乘法 ，如 （1,1）元素值 19 是 `1× 5 + 2× 7 = 19`- 上面三个运算完成了对每一个样例中每个类别交叉熵 `p(x)logq(x)`的计算- 这三步计算分别得到的结果是一个 `n × m`的二维矩阵，n 为一个 batch 中样例的数量， m 为分类的类别数量。- 根据该交叉熵的公式，应该将每行中的 m 个结果相加得到所有样例的交叉熵- 然后再对这 n 行取平均得到一个 batch 的平均交叉熵- 因为分类问题的类别数量是不变的，所以可以直接对整个矩阵做平均而不改变计算结果的意义- 简单展示 `tf.reduce_mean` 函数的使用方法<jupyter_code>v = tf.constant([[1.0,2.0,3.0],[4.0,5.0,6.0]])

with tf.Session() as sess:
    print(tf.reduce_mean(v).eval())<jupyter_output>3.5<jupyter_text>- 因为 交叉熵一般会和 softmax 回归一起使用，所以 Tensorflow 对这两个功能进行了统一封装 ，并提供 `tf.nn.softmax_cross_entopy_with_logits` 函数- 可以通过下面代码来实现使用了 softmax 回归之后的交叉熵损失函数<jupyter_code># y 是预测输出值，y_ 是标准答案
cross_entopy = tf.nn.softmax_cross_entropy_with_logits(y,y_)<jupyter_output><empty_output><jupyter_text>- 回归问题解决的是对具体数值的预测，如房价预测，销量预测等。- 解决回归问题的神经网络一般只有一个输出节点，输出值为预测值。- 回归问题最常用的是损失函数是均方误差（MES, mean squared error）$$ MSE(y,y^{'}) = \frac{\sum_{i=1}^{n}(y_i-y^{'})^2}{n}$$- $y_i$ 为一个 batch 中第 i 个数据的正确答案，$y^{'}$ 为神经网络给出的预测值- Tensorflow 实现均方误差损失函数```mse = tf.reduce_mean(tf.square(y_ - y))```其中：`y_ `代表标准答案， `y` 代表神经网络输出值 4.2.2 自定义损失函数- Tensorflow 支持任意优化的自定义损失函数- 下面公式给出一个 当预测多于真实值和预测少于真实值时有不同系数的损失函数$$ Loss(y, y^{'}) = \sum_{i=1}^{n}f(y_i,y_i^{'}), \space\space  f(x,y)=\left\{\begin{aligned}a(x-y) & & x> y \\b(y-x) & & x≤y \\\end{aligned}\right.  $$- $y_i$ 为一个 batch 中第 i 个数据的正确答案，$y^{'}$ 为神经网络给出的预测值 a,b 是常量，如 a = 10 , b = 1,TF 实现：<jupyter_code>loss = tf.reduce_sum(tf.where(tf.greater(v1,v2),(v1 - v2) * a, (v2- v1) *b ))<jupyter_output><empty_output><jupyter_text>- 用到了 `tf.greater` 和 `tf.where` 来实现选择操作- `tf.greater` 的输入是两个张量，此函数会比较这两个输入张量中每一个元素的大小，并返回比较结果- 当 `tf.greater `  的输入张量维度不一样是，TF 会进行类似 Numpy 广播（broadcasting） 的处理。- `tf.where` 有三个参数，1.选择条件根据，当选择条件 为 True 时，`tf.where` 函数会选择第二个参数中的值，否则使用第三个参数中的值。- 注意：`tf.where` 函数判断和选择都是在元素级别进行，下面展示用法：<jupyter_code>v1 = tf.constant([1.0, 2.0, 3.0, 4.0])
v2 = tf.constant([4.0, 3.0, 2.0, 1.0])

sess = tf.InteractiveSession()

print(tf.greater(v1, v2).eval())

print(tf.where(tf.greater(v1,v2),v1,v2).eval())

sess.close()<jupyter_output>[False False  True  True]
[4. 3. 3. 4.]<jupyter_text>下面通过一个简单的神经网络程序来讲解损失函数对模型训练结果的影响。- 两个输入节点，一个输出节点，没有隐藏层<jupyter_code>import tensorflow as tf
from numpy.random import RandomState

'''
1. 定义神经网络的相关参数和变量。

'''

batch_size = 8

# 输入两个节点
x = tf.placeholder(tf.float32, shape=(None, 2), name='x-input')

# 回归问题一般只有一个输出节点
y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')

# 定义一个单层神经网络的前向传播过程，这里是简单加权和 stddev=1 标准差
w1 = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w1)


'''
2. 设置自定义的损失函数。
 
'''
# 定义预测多了和预测少了的 成本
loss_less = 10 # 预测少了 会损失 10块
loss_more = 1  # 预测多了 只损失 1 块
loss = tf.reduce_sum(tf.where(tf.greater(y,y_),
                             (y - y_) * loss_more,
                             (y_ - y) * loss_less))

train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

'''
3. 生成模拟数据集。
'''

# 通过随机数生成一个数据模拟集 128 个样本，每个样本两个特征
rdm = RandomState(1)

X = rdm.rand(128, 2)

print(X.shape)
# print(X)
'''
设置回归的正确值为两个输入的和加上一个随机量，之所以要加上一个随机量是为了
加入不可预测的噪音，否则不同损失函数的意义就不大了，因为不同损失函数都会在能
完全预测正确的时候最低，一般来说噪音是一个均值为 0 的小量，所以这里的
噪音设置为 -0.05 ~ 0.05 的随机数

'''
# Y 正确值 是一个 list (x1, x2) in X 一个样本有两个特征，128 行 2 列

Y = [[x1 + x2 + (rdm.rand()/10.0-0.05)]  for (x1, x2) in X]

'''
4.训练模型。

'''        

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    STEPS = 5000
    for i in range(STEPS):
        start = (i*batch_size) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
        if i % 1000 == 0:
            print("After %d training step(s), w1 is: " % (i))
            print(sess.run(w1), "\n") 
    print("Final w1 is: \n", sess.run(w1)) 
'''
5. 重新定义损失函数，使得预测多了的损失大，于是模型应该偏向少的方向预测。

'''
loss_less = 1
loss_more = 10 # 预测多了的损失大

loss = tf.reduce_sum(tf.where(tf.greater(y, y_),(y - y_)* loss_more, (y_ - y) * loss_less))
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    STEPS = 5000
    for i in range(STEPS):
        start = (i*batch_size) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(train_step, feed_dict={x:X[start:end], y_:Y[start:end]})
        if i % 1000 == 0:
            print("After %d training step(s), w1 is: " % (i))
            print(sess.run(w1), "\n") 
    
        print("Final w1 is: \n", sess.run(w1))


'''
6. 定义损失函数为MSE。
'''
loss = tf.losses.mean_squared_error(y,y_)
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    STEPS = 5000
    for i in range(STEPS):
        start = (i*batch_size) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
        if i % 1000 == 0:
            print("After %d training step(s), w1 is: " % (i))
            print(sess.run(w1), "\n") 
    
        print("Final w1 is: \n", sess.run(w1))<jupyter_output>After 0 training step(s), w1 is: 
[[-0.81031823]
 [ 1.4855988 ]] 

Final w1 is: 
 [[-0.81031823]
 [ 1.4855988 ]]
Final w1 is: 
 [[-0.80941665]
 [ 1.4865069 ]]
Final w1 is: 
 [[-0.8085683]
 [ 1.4874406]]
Final w1 is: 
 [[-0.80770916]
 [ 1.4883332 ]]
Final w1 is: 
 [[-0.8068043]
 [ 1.4892628]]
Final w1 is: 
 [[-0.805891]
 [ 1.490211]]
Final w1 is: 
 [[-0.8049535]
 [ 1.4911743]]
Final w1 is: 
 [[-0.80400085]
 [ 1.4921538 ]]
Final w1 is: 
 [[-0.80303305]
 [ 1.493138  ]]
Final w1 is: 
 [[-0.80209213]
 [ 1.4940823 ]]
Final w1 is: 
 [[-0.8011821]
 [ 1.4950299]]
Final w1 is: 
 [[-0.8002777]
 [ 1.495945 ]]
Final w1 is: 
 [[-0.7993381]
 [ 1.4968998]]
Final w1 is: 
 [[-0.7984167]
 [ 1.4978073]]
Final w1 is: 
 [[-0.7974996]
 [ 1.4987341]]
Final w1 is: 
 [[-0.7965965]
 [ 1.4996561]]
Final w1 is: 
 [[-0.7957579]
 [ 1.5005246]]
Final w1 is: 
 [[-0.7949343]
 [ 1.5014063]]
Final w1 is: 
 [[-0.7940568]
 [ 1.5023218]]
Final w1 is: 
 [[-0.7931928]
 [ 1.503203 ]]
Final w1 is: 
 [[-0.7922794]
 [ 1.5041058]][...]<jupyter_text>- 第一种得到的 w1= [[1.019347 ],[1.0428089]],也就是得到的预测函数 $y = 1.02x_1 + 1.04x_2$ 比 $x_1 + x_2$ 大，因为在损失函数中，规定的是 预测少了损失更大，所以为了降低损失，预测值会更大些- 第二种，将 loss_more 和 loss_less 调整后，w1 = [[0.9552581],[0.9813394]] ,预测多了损失更大，所以，少预测一些会降低损失- 第三种，使用 MSE 均方误差作为损失函数 [[0.9743756],[1.0243336]] ，离标准答案更近 4.3 神经网络优化算法本节将更加具体的介绍如何通过**反向传播算法（BP backpropgation）** 和**梯度下降算法(gradient descent)**调整神经网络中参数的取值。- 梯度下降算法主要用于优化单个参数的取值- 而反向传播算法给出了一个高效的方式在所有参数上使用梯度下降算法，从而使神经网络模型在训练数据上的损失函数尽可能小- 略（略了很多，看原书）- 只有当损失函数为凸函数时，梯度下降算法才能保证达到全局最优解- GD : 计算时间太长，太慢- SGD：为了加速训练过程，使用随机梯度下降的算法（stochastic gradient descent SGD）这个算法优化的不是在全部的训练数据上的损失函数，而是在每一轮迭代中，随机优化某一条训练数据上的损失函数。缺：在某一条数据上损失函数更小，不代表全部数据上的损失函数更小- mini-batch GD :折中的方法，一小部分数据成为一个 batch，batch-size 是这一小部分的大小，可以大大减小收敛所需要的迭代次数```batch_size = n 每次读取一小部分数据作为当前数据来执行反向传播算法x = tf.placeholder(tf.float32, shape=(batch_size, 2), name='x-input')y_ = tf.placeholder(tf.float32, shape=(batch_size, 2), name='y-input') 定义神经网络结构和优化算法loss = Nonetrain_step = tf.train.AdamOptimizer(0.001).minimize(loss) 训练神经网络with tf.Session() as sess:     参数初始化    None     迭代的更新参数    for i in range(STEPS):         准备 batch_size 个训练数据。         一般将所有的训练数据随机打乱之后再选取可以得到更好的优化效果        current_X, current_Y = ...        sess.run(train_step, feed_dict={x: current_X, y_:current_Y})``` 4.4 神经网络进一步优化本节继续介绍神经网络优化过程中可能遇到的一些问题，以及解决这些问题的常用方法。 4.4.1  学习率的设置如何设置学习率（learning rate）？通过指数衰减的方法来设置梯度下降算法中的学习率，既可以让模型在训练前期快速接近较优解，又可以保证模型在训练后期不会有太大的波动。- 学习率不能过大，也不能过小，Tensorflow 提供了一种更加灵活的学习率设置方法——指数衰减法。- tf.train.exponential_decay 函数实现了指数衰减学习率- exponential_decay 函数会指数级地减小学习率，它实现以下代码的功能```decay_learning_rate = learning_rate & decay_rate ^ (global_step / decay_steps)```    - decay_learning_rate :每一轮优化时使用的学习率    - learning_rate : 初始学习率    - decay_rate ：衰减速度- tf.train.exponential_decay 函数可以通过设置参数 staircase 选择不同的衰减方式，staircase= False ,平滑连续衰减 staircase= True 阶梯状衰减 ![](./images/decay.png)代码示例： 假设我们要最小化函数  $y=x^2$, 选择初始点   $x_0=5$ 1. 学习率为1的时候，x 在 5 和 -5 之间震荡。<jupyter_code>import tensorflow as tf

# 通过 exponential_decay 函数生成学习率

# 使用指数衰减的学习率。在 minimize 函数中传入 global_step 将自动更新
# global_step 参数，从而使得学习率也得到相应更新<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16707295838411979, 'fraction_numerical': 0.06965000870625108, 'mean_word_length': 4.48567335243553, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 37, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '68727', 'license': 'Apache-2.0', 'n_tokens_mistral': 7615, 'n_tokens_neox': 8055, 'n_words': 884, 'path': 'tf_caicloud/Chapter03/.ipynb_checkpoints/chapter_4_Deep NN-checkpoint.ipynb', 'repo_name': 'laobadao/TF_LEARN', 'size': '11486'}