TensorFlow2.0教程-自定義訓練實戰(非tf.keras)
TensorFlow2。0教程-自定義訓練實戰(非tf。keras)
本教程我們將使用TensorFlow來實現鳶尾花分類。整個過程包括:構建模型、模型訓練、模型預測。
最全Tensorflow 2。0 入門教程持續更新:
完整tensorflow2。0教程程式碼請看
https://
github。com/czy36mengfei
/tensorflow2_tutorials_chinese
(歡迎star)
本教程主要由tensorflow2。0官方教程的個人學習復現筆記整理而來,中文講解,方便喜歡閱讀中文教程的朋友,官方教程:https://www。tensorflow。org
匯入相關庫
匯入TensorFlow和其他所需的Python模組。 預設情況下,TensorFlow2使用急切執行來程式,會立即返回結果。
from
__future__
import
absolute_import
,
division
,
print_function
,
unicode_literals
import
os
import
matplotlib。pyplot
as
plt
!
pip
install
-
q
tensorflow
==
2。0
。
0
-
alpha0
import
tensorflow
as
tf
(
‘tf version:’
,
tf
。
__version__
)
(
‘eager execution:’
,
tf
。
executing_eagerly
())
tf
version
:
2。0
。
0
-
alpha0
eager
execution
:
True
鳶尾花分類問題
想象一下,你是一名植物學家,正在尋找一種自動化的方法來對你找到的每種鳶尾花進行分類。 機器學習提供了許多演算法來對花進行統計分類。 例如,複雜的機器學習程式可以基於照片對花進行分類。而這裡,我們將根據萼片和花瓣的長度和寬度測量來對鳶尾花進行分類。
鳶尾花有300多種類別,但我們的這裡主要對以下三種進行分類: - Iris setosa - Iris virginica - Iris versicolor
幸運的是,有人已經用萼片和花瓣測量建立了120個鳶尾花的資料集。 這是一個流行的初學者機器學習分類問題的經典資料集。
下載資料集 使用tf。keras。utils。get_file函式下載訓練資料集檔案。 這將返回下載檔案的檔案路徑。
train_dataset_url
=
“https://storage。googleapis。com/download。tensorflow。org/data/iris_training。csv”
train_dataset_fp
=
tf
。
keras
。
utils
。
get_file
(
fname
=
os
。
path
。
basename
(
train_dataset_url
),
origin
=
train_dataset_url
)
(
‘下載資料至:’
,
train_dataset_fp
)
下載資料至:
/
root
/。
keras
/
datasets
/
iris_training
。
csv
檢查資料
此資料集iris_training。csv是一個純文字檔案,用於儲存格式為逗號分隔值(CSV)的表格資料。 使用head -n5命令在前五個條目中取一個峰值:
!
head
-
n5
{
train_dataset_fp
}
120
,
4
,
setosa
,
versicolor
,
virginica
6。4
,
2。8
,
5。6
,
2。2
,
2
5。0
,
2。3
,
3。3
,
1。0
,
1
4。9
,
2。5
,
4。5
,
1。7
,
2
4。9
,
3。1
,
1。5
,
0。1
,
0
從資料集的此檢視中,請注意以下內容:
第一行是包含有關資料集的資訊的標題: 總共有120個例子。 每個示例都有四個特徵和三個可能的標籤名稱之一。 後續行是資料記錄,每行一個示例,其中: 前四個欄位是特徵:這些是示例的特徵。 這裡,欄位包含代表花卉測量值的浮點數。 最後一列是標籤:這是我們想要預測的值。 對於此資料集,它是與花名稱對應的整數值0,1或2。
column_names
=
[
‘sepal_length’
,
‘sepal_width’
,
‘petal_length’
,
‘petal_width’
,
‘species’
]
# 獲取特徵和標籤名
feature_name
=
column_names
[:
-
1
]
label_name
=
column_names
[
-
1
]
每個標籤都與字串名稱相關聯(例如,“setosa”),但機器學習通常依賴於數值。使用標籤數字來對映類別,例如:
0:Iris setosa:
1:Iris versicolor
2:Iris virginica
class_names
=
[
‘Iris setosa’
,
‘Iris versicolor’
,
‘Iris virginica’
]
建立一個 tf。data。Dataset
TensorFlow的資料集API處理許多將資料載入到模型中的常見情況。這是一個高階API,用於讀取資料並將其轉換為用於訓練的資料型別。
由於資料集是CSV格式的文字檔案,因此需要使用make_csv_dataset函式將資料解析為合適的格式。由於此函式為訓練模型生成資料,因此預設行為是對資料(shuffle=True, shuffle_buffer_size=10000)進行混洗,並永遠重複資料集(num_epochs=None)。同時還需要設定batch_size引數。
batch_size
=
32
train_dataset
=
tf
。
data
。
experimental
。
make_csv_dataset
(
train_dataset_fp
,
batch_size
,
column_names
=
column_names
,
label_name
=
label_name
,
num_epochs
=
1
)
該make_csv_dataset函式返回tf。data。Dataset的(features, label)對,其中features是一個字典:{‘feature_name’: value}
而這些Dataset物件是可迭代的。
features
,
labels
=
next
(
iter
(
train_dataset
))
(
features
)
OrderedDict
([(
‘sepal_length’
,
<
tf
。
Tensor
:
id
=
64
,
shape
=
(
32
,),
dtype
=
float32
,
numpy
=
array
([
7。6
,
6。9
,
7。2
,
5。
,
6。7
,
4。8
,
5。4
,
5。1
,
7。7
,
6。
,
6。3
,
7。4
,
5。2
,
7。2
,
6。7
,
6。1
,
5。
,
4。9
,
6。2
,
4。5
,
6。6
,
6。
,
5。5
,
6。3
,
4。8
,
6。7
,
6。1
,
5。6
,
7。3
,
6。9
,
5。7
,
6。3
],
dtype
=
float32
)
>
),
(
‘sepal_width’
,
<
tf
。
Tensor
:
id
=
65
,
shape
=
(
32
,),
dtype
=
float32
,
numpy
=
array
([
3。
,
3。2
,
3。6
,
2。3
,
3。
,
3。
,
3。9
,
3。7
,
3。
,
2。2
,
2。3
,
2。8
,
2。7
,
3。2
,
3。1
,
2。8
,
3。4
,
3。1
,
2。8
,
2。3
,
3。
,
3。
,
3。5
,
3。3
,
3。4
,
3。
,
2。8
,
2。9
,
2。9
,
3。1
,
3。8
,
2。5
],
dtype
=
float32
)
>
),
(
‘petal_length’
,
<
tf
。
Tensor
:
id
=
62
,
shape
=
(
32
,),
dtype
=
float32
,
numpy
=
array
([
6。6
,
5。7
,
6。1
,
3。3
,
5。2
,
1。4
,
1。3
,
1。5
,
6。1
,
5。
,
4。4
,
6。1
,
3。9
,
6。
,
5。6
,
4。
,
1。6
,
1。5
,
4。8
,
1。3
,
4。4
,
4。8
,
1。3
,
6。
,
1。6
,
5。
,
4。7
,
3。6
,
6。3
,
4。9
,
1。7
,
5。
],
dtype
=
float32
)
>
),
(
‘petal_width’
,
<
tf
。
Tensor
:
id
=
63
,
shape
=
(
32
,),
dtype
=
float32
,
numpy
=
array
([
2。1
,
2。3
,
2。5
,
1。
,
2。3
,
0。3
,
0。4
,
0。4
,
2。3
,
1。5
,
1。3
,
1。9
,
1。4
,
1。8
,
2。4
,
1。3
,
0。4
,
0。1
,
1。8
,
0。3
,
1。4
,
1。8
,
0。2
,
2。5
,
0。2
,
1。7
,
1。2
,
1。3
,
1。8
,
1。5
,
0。3
,
1。9
],
dtype
=
float32
)
>
)])
相同的特徵被放在同一個陣列中,陣列維度為batch_size大小。 可以視覺化如圖:
plt
。
scatter
(
features
[
‘petal_length’
],
features
[
‘sepal_length’
],
c
=
labels
,
cmap
=
‘viridis’
)
plt
。
xlabel
(
“Petal length”
)
plt
。
ylabel
(
“Sepal length”
)
plt
。
show
()
一般我們會把同一個資料的不同feature放在同一個陣列中,我們使用tf。pack()來將features重構為(batch_size, num_features)形狀。
def
pack_features_vector
(
features
,
labels
):
features
=
tf
。
stack
(
list
(
features
。
values
()),
axis
=
1
)
return
features
,
labels
# 使用tf。data。Dataset。map將重構函式運用到每條資料中。
train_dataset
=
train_dataset
。
map
(
pack_features_vector
)
# 檢視前5個數據
features
,
labels
=
next
(
iter
(
train_dataset
))
(
features
[:
5
])
tf
。
Tensor
(
[[
7。6
3。
6。6
2。1
]
[
6。9
3。2
5。7
2。3
]
[
7。2
3。6
6。1
2。5
]
[
5。
2。3
3。3
1。
]
[
6。7
3。
5。2
2。3
]],
shape
=
(
5
,
4
),
dtype
=
float32
)
選擇模型
一個模型是功能和標籤之間的關係。對於鳶尾花分類問題,該模型定義了萼片和花瓣測量與預測的鳶尾花物種之間的關係。一些簡單的模型可以用幾行代數來描述,但是複雜的機器學習模型具有很多難以概括的引數。
我們能否在不使用機器學習的情況下確定四種特徵與虹膜物種之間的關係?也就是說,可以使用傳統的程式設計技術(例如,很多條件語句)來建立模型嗎?也許 - 如果你對資料集進行了足夠長的分析,以確定特定物種的花瓣和萼片測量值之間的關係。這對於更復雜的資料集來說變得困難 - 可能是不可能的。良好的機器學習方法可以為我們確定模型。如果我們將足夠的代表性示例提供給正確的機器學習模型型別,程式將為我們找出關係。
選擇具體模型
我們需要選擇要訓練的模型。有很多型別的模型和挑選一個好的經驗。本教程使用神經網路來解決鳶尾花I分類問題。神經網路可以找到特徵和標籤之間的複雜關係。它是一個高度結構化的圖,組織成一個或多個隱藏層。每個隱藏層由一個或多個神經元組成。有幾類神經網路,這個程式使用密集或完全連線的神經網路:一層中的神經元接收來自每個神經網路的輸入連線上一層的神經元。例如,圖2說明了一個由輸入層,兩個隱藏層和一個輸出層組成的密集神經網路:
圖2。具有特徵,隱藏層和預測的神經網路。
當對來自圖2的模型進行訓練並喂入未標記的示例時,它產生三個預測:該花是給定的鳶尾花物種的可能性。這種預測稱為推理。對於此示例,輸出預測的總和為1。0。在圖2中,該預測分解為:0。02對山鳶尾,0。95對於變色鳶尾,並0。03為錦葵鳶尾。這意味著模型以95%的機率預測 - 未標記的示例花是變色鳶尾。
使用Keras建立模型
TensorFlow tf。keras API是建立模型和圖層的首選方式。這使得構建模型和實驗變得容易,而Keras處理將所有內容連線在一起的複雜性。
該tf。keras。Sequential模型是層的線性堆疊。它的建構函式採用一個層例項列表,在這種情況下,兩個Dense層各有10個節點,一個輸出層有3個節點代表我們的標籤預測。第一層的input_shape引數對應於資料集中的要素數,並且是必需的。
# 構建線性模型
model
=
tf
。
keras
。
Sequential
([
tf
。
keras
。
layers
。
Dense
(
10
,
activation
=
‘relu’
,
input_shape
=
(
4
,)),
tf
。
keras
。
layers
。
Dense
(
10
,
activation
=
‘relu’
),
tf
。
keras
。
layers
。
Dense
(
3
)
])
啟用函式確定在層中的每個節點的輸出形狀。這些非線性很重要 - 沒有它們,模型將等同於單個層。有許多可用的啟用,但ReLU對於隱藏層是常見的。
隱藏層和神經元的理想數量取決於問題和資料集。像機器學習的許多方面一樣,選擇神經網路的最佳形狀需要知識和實驗經驗。根據經驗,增加隱藏層和神經元的數量通常會建立一個更強大的模型,這需要更多的資料來有效地訓練。
測試模型結構
prediction
=
model
(
features
)
prediction
[:
5
]
<
tf
。
Tensor
:
id
=
229
,
shape
=
(
5
,
3
),
dtype
=
float32
,
numpy
=
array
([[
1。6543204
,
0。12405288
,
0。24490094
],
[
1。4488522
,
0。11291474
,
0。24872684
],
[
1。5161525
,
0。11867774
,
0。28899187
],
[
0。86002606
,
0。05858952
,
0。06260413
],
[
1。3767202
,
0。10884094
,
0。21688706
]],
dtype
=
float32
)
>
多分類任務需要使用softmax進行歸一化
tf
。
nn
。
softmax
(
prediction
)[:
5
]
<
tf
。
Tensor
:
id
=
235
,
shape
=
(
5
,
3
),
dtype
=
float32
,
numpy
=
array
([[
0。6845738
,
0。148195
,
0。16723117
],
[
0。63935834
,
0。16809471
,
0。19254689
],
[
0。6492055
,
0。16049689
,
0。1902975
],
[
0。52654505
,
0。23625231
,
0。23720267
],
[
0。62697244
,
0。1764475
,
0。19658
]],
dtype
=
float32
)
>
使用tf。argmax獲取機率最大的類標籤
(
‘prediction:’
,
tf
。
argmax
(
prediction
,
axis
=
1
))
(
‘label:’
,
labels
)
prediction
:
tf
。
Tensor
([
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
],
shape
=
(
32
,),
dtype
=
int64
)
label
:
tf
。
Tensor
([
2
2
2
1
2
0
0
0
2
2
1
2
1
2
2
1
0
0
2
0
1
2
0
2
0
1
1
1
2
1
0
2
],
shape
=
(
32
,),
dtype
=
int32
)
訓練模型
訓練是機器學習中模型從資料集中學習知識並最佳化自身能力的過程。
Iris分類問題是一個典型的監督學習問題,其透過包含標籤的資料集進行學習。而無監督學習則是僅從特徵中去尋找相應的模式。
訓練和評估的過程都需要計算模型的損失,它可以衡量預測與正確標籤的差距,訓練過程都是要最小化損失。
我們後面將直接使用tf。keras裡面包裝好的損失函式來計算損失。
# 損失函式
loss_object
=
tf
。
keras
。
losses
。
SparseCategoricalCrossentropy
(
from_logits
=
True
)
# 獲取損失
def
loss
(
model
,
x
,
y
):
y_
=
model
(
x
)
return
loss_object
(
y_true
=
y
,
y_pred
=
y_
)
l
=
loss
(
model
,
features
,
labels
)
(
l
)
tf
。
Tensor
(
1。3738844
,
shape
=
(),
dtype
=
float32
)
使用tf。GradientTape計算loss對所有變數的梯度。
def
grad
(
model
,
inputs
,
targets
):
with
tf
。
GradientTape
()
as
tape
:
loss_value
=
loss
(
model
,
inputs
,
targets
)
return
loss_value
,
tape
。
gradient
(
loss_value
,
model
。
trainable_variables
)
建立最佳化器
最佳化程式將計算出的梯度應用於模型的變數,以最大限度地減少損失函式。 您可以將損失函式視為曲面(參見圖3),我們希望透過四處走動找到它的最低點。 漸變指向最陡的上升方向 - 所以我們將以相反的方向行進並向下移動。 透過迭代計算每批的損失和梯度,我們將在訓練期間調整模型。 逐漸地,該模型將找到權重和偏差的最佳組合,以最小化損失。 損失越低,模型的預測越好。
TensorFlow有許多可用於訓練的最佳化演算法。 該模型使用tf。train。GradientDescentOptimizer實現隨機梯度下降(SGD)演算法。 learning_rate設定每次迭代下一步的步長。 這是一個超引數,您通常會調整以獲得更好的結果。
optimizer
=
tf
。
keras
。
optimizers
。
Adam
(
learning_rate
=
0。01
)
最佳化器使用如下
loss_value
,
grads
=
grad
(
model
,
features
,
labels
)
(
‘步數:{}, 初始loss值:{}’
。
format
(
optimizer
。
iterations
。
numpy
(),
loss_value
。
numpy
()))
optimizer
。
apply_gradients
(
zip
(
grads
,
model
。
trainable_variables
))
(
‘步數:{}, loss值:{}’
。
format
(
optimizer
。
iterations
。
numpy
(),
loss
(
model
,
features
,
labels
)
。
numpy
()))
步數:
0
,
初始
loss值
:
1。3738844394683838
步數:
1
,
loss值
:
1。1648454666137695
訓練迴圈
每個epoch資料將會被訓練一次。
# 儲存loss和acc
train_loss_results
=
[]
train_accuracy_results
=
[]
num_epochs
=
201
for
epoch
in
range
(
num_epochs
):
# 用於記錄loss和acc的類
epoch_loss_avg
=
tf
。
keras
。
metrics
。
Mean
()
epoch_accuracy
=
tf
。
keras
。
metrics
。
SparseCategoricalAccuracy
()
# 訓練迴圈
for
x
,
y
in
train_dataset
:
# 獲取loss和梯度
loss_value
,
grads
=
grad
(
model
,
x
,
y
)
# 梯度最佳化
optimizer
。
apply_gradients
(
zip
(
grads
,
model
。
trainable_variables
))
# 記錄loss均值
epoch_loss_avg
(
loss_value
)
# 記錄準確率
epoch_accuracy
(
y
,
model
(
x
))
# 儲存每個epoch的loss和acc
train_loss_results
。
append
(
epoch_loss_avg
。
result
())
train_accuracy_results
。
append
(
epoch_accuracy
。
result
())
if
epoch
%
50
==
0
:
(
“Epoch {:03d}: Loss: {:。3f}, Accuracy: {:。3%}”
。
format
(
epoch
,
epoch_loss_avg
。
result
(),
epoch_accuracy
。
result
()))
Epoch
000
:
Loss
:
1。048
,
Accuracy
:
70。000
%
Epoch
050
:
Loss
:
0。074
,
Accuracy
:
99。167
%
Epoch
100
:
Loss
:
0。059
,
Accuracy
:
99。167
%
Epoch
150
:
Loss
:
0。054
,
Accuracy
:
99。167
%
Epoch
200
:
Loss
:
0。051
,
Accuracy
:
99。167
%
視覺化訓練過程
fig
,
axes
=
plt
。
subplots
(
2
,
sharex
=
True
,
figsize
=
(
12
,
8
))
fig
。
suptitle
(
‘Training Metrics’
)
axes
[
0
]
。
set_ylabel
(
“Loss”
,
fontsize
=
14
)
axes
[
0
]
。
plot
(
train_loss_results
)
axes
[
1
]
。
set_ylabel
(
“Accuracy”
,
fontsize
=
14
)
axes
[
1
]
。
set_xlabel
(
“Epoch”
,
fontsize
=
14
)
axes
[
1
]
。
plot
(
train_accuracy_results
)
plt
。
show
()
評估模型
評估模型類似於訓練模型。 最大的區別是示例來自單獨的測試集而不是訓練集。 為了公平地評估模型的有效性,用於評估模型的示例必須與用於訓練模型的示例不同。
測試資料集的設定類似於訓練資料集的設定。 下載CSV文字檔案並解析該值,然後將其打亂:
test_url
=
“https://storage。googleapis。com/download。tensorflow。org/data/iris_test。csv”
test_fp
=
tf
。
keras
。
utils
。
get_file
(
fname
=
os
。
path
。
basename
(
test_url
),
origin
=
test_url
)
Downloading
data
from
https
:
//
storage
。
googleapis
。
com
/
download
。
tensorflow
。
org
/
data
/
iris_test
。
csv
8192
/
573
[
============================================================================================================================================================================================================================================================================================================================================================================================================================================
]
-
0
s
0
us
/
step
test_dataset
=
tf
。
data
。
experimental
。
make_csv_dataset
(
test_fp
,
batch_size
,
column_names
=
column_names
,
label_name
=
‘species’
,
num_epochs
=
1
,
shuffle
=
False
)
test_dataset
=
test_dataset
。
map
(
pack_features_vector
)
評估測試資料集上的模型
與訓練階段不同,該模型僅評估測試資料的單個時期。 在下面的程式碼單元格中,我們迭代測試集中的每個示例,並將模型的預測與實際標籤進行比較。 這用於測量整個測試集中模型的準確性。
# 準確率統計類
test_accuracy
=
tf
。
keras
。
metrics
。
Accuracy
()
for
(
x
,
y
)
in
test_dataset
:
logits
=
model
(
x
)
prediction
=
tf
。
argmax
(
logits
,
axis
=
1
,
output_type
=
tf
。
int32
)
test_accuracy
(
prediction
,
y
)
(
‘測試集準確率:’
,
test_accuracy
。
result
())
測試集準確率:
tf
。
Tensor
(
0。96666664
,
shape
=
(),
dtype
=
float32
)
結果對比
tf
。
stack
([
y
,
prediction
],
axis
=
1
)
<
tf
。
Tensor
:
id
=
164737
,
shape
=
(
30
,
2
),
dtype
=
int32
,
numpy
=
array
([[
1
,
1
],
[
2
,
2
],
[
0
,
0
],
[
1
,
1
],
[
1
,
1
],
[
1
,
1
],
[
0
,
0
],
[
2
,
1
],
[
1
,
1
],
[
2
,
2
],
[
2
,
2
],
[
0
,
0
],
[
2
,
2
],
[
1
,
1
],
[
1
,
1
],
[
0
,
0
],
[
1
,
1
],
[
0
,
0
],
[
0
,
0
],
[
2
,
2
],
[
0
,
0
],
[
1
,
1
],
[
2
,
2
],
[
1
,
1
],
[
1
,
1
],
[
1
,
1
],
[
0
,
0
],
[
1
,
1
],
[
2
,
2
],
[
1
,
1
]],
dtype
=
int32
)
>
使用訓練的模型進行預測
我們已經訓練了一個模型並且“證明”它對Iris物種進行分類是好的 - 但不是完美的。 現在讓我們使用訓練有素的模型對未標記的例子做出一些預測; 也就是說,包含特徵但不包含標籤的示例。
在現實生活中,未標記的示例可能來自許多不同的來源,包括應用程式,CSV檔案和資料來源。 目前,我們將手動提供三個未標記的示例來預測其標籤。 回想一下,標籤號被對映到命名錶示,如下所示: - 0: Iris setosa - 1: Iris versicolor - 2: Iris virginica
predict_dataset
=
tf
。
convert_to_tensor
([
[
5。1
,
3。3
,
1。7
,
0。5
,],
[
5。9
,
3。0
,
4。2
,
1。5
,],
[
6。9
,
3。1
,
5。4
,
2。1
]
])
predictions
=
model
(
predict_dataset
)
for
i
,
logits
in
enumerate
(
predictions
):
class_idx
=
tf
。
argmax
(
logits
)
。
numpy
()
p
=
tf
。
nn
。
softmax
(
logits
)[
class_idx
]
name
=
class_names
[
class_idx
]
(
“Example {} prediction: {} ({:4。1f}%)”
。
format
(
i
,
name
,
100
*
p
))
Example
0
prediction
:
Iris
setosa
(
99。9
%
)
Example
1
prediction
:
Iris
versicolor
(
99。9
%
)
Example
2
prediction
:
Iris
virginica
(
99。1
%
)