# 實現反向傳播
使用 TensorFlow 的一個好處是它可以跟蹤操作并根據反向傳播自動更新模型變量。在本文中,我們將介紹如何在訓練機器學習模型時將此方面用于我們的優勢。
## 做好準備
現在,我們將介紹如何以最小化損失函數的方式更改模型中的變量。我們已經學會了如何使用對象和操作,并創建了測量我們的預測和目標之間距離的損失函數。現在,我們只需告訴 TensorFlow 如何通過我們的計算圖反向傳播錯誤來更新變量并最小化損失函數。這是通過聲明優化函數完成的。一旦我們聲明了一個優化函數,TensorFlow 將通過并計算出圖中所有計算的反向傳播項。當我們輸入數據并最小化 los 函數時,TensorFlow 將相應地修改圖中的變量。
對于這個秘籍,我們將做一個非常簡單的回歸算法。我們將從正態分布中抽取隨機數,均值為 1,標準差為 0.1。然后,我們將通過一個操作來運行數字,這將是它們乘以變量`A`。由此,損失函數將是輸出和目標之間的 L2 范數,其總是值 10.理論上,A 的最佳值將是數字 10,因為我們的數據將具有平均值 1。
第二個例子是一個非常簡單的二分類算法。在這里,我們將從兩個正態分布`N(-1,1)`和`N(3,1)`生成 100 個數字。來自`N(-1, 1)`的所有數字將在目標等級 0 中,并且來自`N(3, 1)`的所有數字將在目標等級 1 中。用于區分這些數字的模型將是翻譯的 S 形函數。換句話說,模型將是`sigmoid(x + A)`,其中`A`是我們將適合的變量。從理論上講,A 將等于-1。我們得到這個數字是因為如果`m1`和`m2`是兩個正常函數的平均值,那么加到它們以將它們等距離轉換為零的值將是 - `(m1 + m2) / 2`。我們將在第二個例子中看到 TensorFlow 如何達到該數字。
雖然指定良好的學習率有助于算法的收斂,但我們還必須指定一種優化。從前兩個例子中,我們使用標準梯度下降。這是通過`GradientDescentOptimizer()` TensorFlow 函數實現的。
## 操作步驟
以下是回歸示例的工作原理:
1. 我們首先加載`numpy`和`tensorflow`數值 Python 包:
```py
import numpy as np
import tensorflow as tf
```
1. 現在,我們開始圖會話:
```py
sess = tf.Session()
```
1. 接下來,我們創建數據,占位符和`A`變量:
```py
x_vals = np.random.normal(1, 0.1, 100)
y_vals = np.repeat(10., 100)
x_data = tf.placeholder(shape=[1], dtype=tf.float32)
y_target = tf.placeholder(shape=[1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1]))
```
1. 我們將乘法運算添加到圖中:
```py
my_output = tf.mul(x_data, A)
```
1. 接下來,我們在乘法輸出和目標數據之間添加 L2 `Loss`函數:
```py
loss = tf.square(my_output - y_target)
```
1. 現在,我們必須聲明一種優化圖中變量的方法。我們聲明了一種優化算法。大多數優化算法需要知道每次迭代中的步進距離。該距離由學習率控制。如果我們的學習率太大,我們的算法可能會超過最小值,但如果我們的學習率太小,我們的算法可能需要很長時間才能收斂;這與消失和爆炸的梯度問題有關。學習率對收斂有很大影響,我們將在本節末尾討論這個問題。雖然我們在這里使用標準梯度下降算法,但是有許多不同的優化算法可以不同地運行,并且可以根據問題做得更好或更差。有關不同優化算法的精彩概述,請參閱 Sebastian Ruder 在本文末尾的另請參閱部分中的文章:
```py
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.02)
train_step = my_opt.minimize(loss)
```
1. 現在我們可以初始化我們的模型變量:
```py
init = tf.global_variable_initializer()
sess.run(init)
```
There is a lot of theory on which learning rates are best. This is one of the harder things to figure out in machine learning algorithms. Good papers to read about how learning rates are related to specific optimization algorithms are listed in the See also section at the end of this recipe.
1. 最后一步是循環我們的訓練算法并告訴 TensorFlow 多次訓練。我們將這樣做 101 次,并且每 25 次迭代打印出結果。為了訓練,我們將選擇隨機`x`和`y`條目并通過圖提供。 TensorFlow 將自動計算損失,并略微改變`A`偏差以最小化損失:
```py
for i in range(100):
rand_index = np.random.choice(100)
rand_x = [x_vals[rand_index]]
rand_y = [y_vals[rand_index]]
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i + 1) % 25 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
print('Loss = ' + str(sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})))
# Here is the output:
Step #25 A = [ 6.23402166]
Loss = 16.3173
Step #50 A = [ 8.50733757]
Loss = 3.56651
Step #75 A = [ 9.37753201]
Loss = 3.03149
Step #100 A = [ 9.80041122]
Loss = 0.0990248
```
現在,我們將介紹簡單分類示例的代碼。如果我們先重置圖,我們可以使用相同的 TensorFlow 腳本。請記住,我們將嘗試找到一個最佳平移`A`,它將兩個分布轉換為原點,而 sigmoid 函數將兩個分為兩個不同的類:
1. 首先,我們重置圖并重新初始化圖會話:
```py
from tensorflow.python.framework import ops
ops.reset_default_graph()
sess = tf.Session()
```
1. 接下來,我們從兩個不同的正態分布`N(-1, 1)`和`N(3, 1)`中提取數據。我們還將生成目標標簽,數據占位符和偏差變量`A`:
```py
x_vals = np.concatenate((np.random.normal(-1, 1, 50), np.random.normal(3, 1, 50)))
y_vals = np.concatenate((np.repeat(0., 50), np.repeat(1., 50)))
x_data = tf.placeholder(shape=[1], dtype=tf.float32)
y_target = tf.placeholder(shape=[1], dtype=tf.float32)
A = tf.Variable(tf.random_normal(mean=10, shape=[1]))
```
> 我們將`A`初始化為大約 10 的值,遠離理論值-1。我們這樣做的目的是為了說明算法如何從 10 的值收斂到最佳值-1。
1. 接下來,我們將轉換操作添加到圖中。請記住,我們不必將它包裝在 sigmoid 函數中,因為 loss 函數將為我們執行此操作:
```py
my_output = tf.add(x_data, A)
```
1. 由于特定損失函數需要具有與之關聯的額外維度的批量數據(添加的維度,即批次編號),因此我們將使用`expand_dims()`函數為輸出添加額外維度。在下一節中,我們將討論如何在訓練中使用可變大小的批次。現在,我們將再次使用一個隨機數據點:
```py
my_output_expanded = tf.expand_dims(my_output, 0)
y_target_expanded = tf.expand_dims(y_target, 0)
```
1. 接下來,我們將初始化我們的一個變量`A`:
```py
init = tf.initialize_all_variables()
sess.run(init)
```
1. 現在,我們宣布我們的損失函數。我們將使用帶有未縮放的 logits 的交叉熵,它使用 sigmoid 函數對它們進行轉換。在名為`nn.sigmoid_cross_entropy_with_logits()`的神經網絡包中,TensorFlow 為我們提供了這一函數。如前所述,它希望參數具有特定的維度,因此我們必須相應地使用擴展的輸出和目標:
```py
xentropy = tf.nn.sigmoid_cross_entropy_with_logits( my_output_expanded, y_target_expanded)
```
1. 與回歸示例一樣,我們需要向圖中添加優化器函數,以便 TensorFlow 知道如何更新圖中的偏差變量:
```py
my_opt = tf.train.GradientDescentOptimizer(0.05)
train_step = my_opt.minimize(xentropy)
```
1. 最后,我們循環遍歷隨機選擇的數據點數百次并相應地更新`A`變量。每 200 次迭代,我們將打印出`A`的值和損失:
```py
for i in range(1400):
rand_index = np.random.choice(100)
rand_x = [x_vals[rand_index]]
rand_y = [y_vals[rand_index]]
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
if (i + 1) % 200 == 0:
print('Step #' + str(i+1) + ' A = ' + str(sess.run(A)))
print('Loss = ' + str(sess.run(xentropy, feed_dict={x_data: rand_x, y_target: rand_y})))
Step #200 A = [ 3.59597969]
Loss = [[ 0.00126199]]
Step #400 A = [ 0.50947344]
Loss = [[ 0.01149425]]
Step #600 A = [-0.50994617]
Loss = [[ 0.14271219]]
Step #800 A = [-0.76606178]
Loss = [[ 0.18807337]]
Step #1000 A = [-0.90859312]
Loss = [[ 0.02346182]]
Step #1200 A = [-0.86169094]
Loss = [[ 0.05427232]]
Step #1400 A = [-1.08486211]
Loss = [[ 0.04099189]]
```
## 工作原理
有關回顧和解釋,對于這兩個示例,我們執行了以下操作:
1. 創建了數據。這兩個示例都需要通過占位符加載數據。
2. 初始化占位符和變量。這些是非常相似的數據占位符。變量非常相似,它們都有乘法矩陣`A`,但第一個分類算法有一個偏差項來找到數據中的分裂。
3. 創建了損失函數,我們使用 L2 損失進行回歸,使用交叉熵損失進行分類。
4. 定義了一種優化算法。兩種算法都使用梯度下降。
5. 迭代隨機數據樣本以迭代更新我們的變量。
## 更多
如前所述,優化算法對學習率的選擇很敏感。重要的是要以簡潔的方式總結這種選擇的效果:
| 學習率大小 | 優點缺點 | 用途 |
| --- | --- | --- |
| 較小的學習率 | 收斂速度較慢但結果更準確 | 如果解決方案不穩定,請先嘗試降低學習率 |
| 學習率更高 | 不太準確,但收斂速度更快 | 對于某些問題,有助于防止解決方案停滯不前 |
有時,標準梯度下降算法會顯著卡住或減速。當優化卡在馬鞍的平坦點時,可能會發生這種情況。為了解決這個問題,還有另一種算法考慮了動量項,它增加了前一步驟的梯度下降值的一小部分。 TensorFlow 內置了`MomentumOptimizer()`函數。
另一種變體是為我們模型中的每個變量改變優化器步驟。理想情況下,我們希望為較小的移動變量采取較大的步驟,為較快的變化變量采取較短的步驟。我們不會深入研究這種方法的數學,但這種思想的常見實現稱為 Adagrad 算法。該算法考慮了變量梯度的整個歷史。 TensorFlow 中的函數稱為`AdagradOptimizer()`。
有時候,Adagrad 會過早地強調梯度為零,因為它考慮了整個歷史。解決方法是限制我們使用的步數。這樣做稱為 Adadelta 算法。我們可以使用`AdadeltaOptimizer()`函數來應用它。
還有一些不同的梯度下降算法的其他實現。對于這些,我們會將讀者引用到 TensorFlow 文檔: [https://www.tensorflow.org/api_guides/python/train](https://www.tensorflow.org/api_guides/python/train) 。
## 另見
有關優化算法和學習率的一些參考,請參閱以下文章和文章:
* 另見本章的秘籍如下:
* 在實現損失函數部分。
* 在實現反向傳播部分。
* Kingma,D.,Jimmy,L。Adam:一種隨機優化方法。 ICLR 2015 [https://arxiv.org/pdf/1412.6980.pdf](https://arxiv.org/pdf/1412.6980.pdf)
* Ruder,S。梯度下降優化算法概述。 2016 [https://arxiv.org/pdf/1609.04747v1.pdf](https://arxiv.org/pdf/1609.04747v1.pdf)
* Zeiler,M。ADADelta:一種自適應學習率方法。 2012 [http://www.matthewzeiler.com/pubs/googleTR2012/googleTR2012.pdf](http://www.matthewzeiler.com/pubs/googleTR2012/googleTR2012.pdf)
- TensorFlow 入門
- 介紹
- TensorFlow 如何工作
- 聲明變量和張量
- 使用占位符和變量
- 使用矩陣
- 聲明操作符
- 實現激活函數
- 使用數據源
- 其他資源
- TensorFlow 的方式
- 介紹
- 計算圖中的操作
- 對嵌套操作分層
- 使用多個層
- 實現損失函數
- 實現反向傳播
- 使用批量和隨機訓練
- 把所有東西結合在一起
- 評估模型
- 線性回歸
- 介紹
- 使用矩陣逆方法
- 實現分解方法
- 學習 TensorFlow 線性回歸方法
- 理解線性回歸中的損失函數
- 實現 deming 回歸
- 實現套索和嶺回歸
- 實現彈性網絡回歸
- 實現邏輯回歸
- 支持向量機
- 介紹
- 使用線性 SVM
- 簡化為線性回歸
- 在 TensorFlow 中使用內核
- 實現非線性 SVM
- 實現多類 SVM
- 最近鄰方法
- 介紹
- 使用最近鄰
- 使用基于文本的距離
- 使用混合距離函數的計算
- 使用地址匹配的示例
- 使用最近鄰進行圖像識別
- 神經網絡
- 介紹
- 實現操作門
- 使用門和激活函數
- 實現單層神經網絡
- 實現不同的層
- 使用多層神經網絡
- 改進線性模型的預測
- 學習玩井字棋
- 自然語言處理
- 介紹
- 使用詞袋嵌入
- 實現 TF-IDF
- 使用 Skip-Gram 嵌入
- 使用 CBOW 嵌入
- 使用 word2vec 進行預測
- 使用 doc2vec 進行情緒分析
- 卷積神經網絡
- 介紹
- 實現簡單的 CNN
- 實現先進的 CNN
- 重新訓練現有的 CNN 模型
- 應用 StyleNet 和 NeuralStyle 項目
- 實現 DeepDream
- 循環神經網絡
- 介紹
- 為垃圾郵件預測實現 RNN
- 實現 LSTM 模型
- 堆疊多個 LSTM 層
- 創建序列到序列模型
- 訓練 Siamese RNN 相似性度量
- 將 TensorFlow 投入生產
- 介紹
- 實現單元測試
- 使用多個執行程序
- 并行化 TensorFlow
- 將 TensorFlow 投入生產
- 生產環境 TensorFlow 的一個例子
- 使用 TensorFlow 服務
- 更多 TensorFlow
- 介紹
- 可視化 TensorBoard 中的圖
- 使用遺傳算法
- 使用 k 均值聚類
- 求解常微分方程組
- 使用隨機森林
- 使用 TensorFlow 和 Keras