一、Model類
(1)初始化方法
1.可擴(kuò)展參數(shù):從kwargs字典里獲取,可限制key的取值
-
name:model變量域scope名稱,獲取不到采用self.__class__.__name__.lower()取類的名字 -
logging: 是否開啟TensorBoard直方圖儀表板,獲取不到為False
- 其他參數(shù)
vars: name scope下的變量(字典)
placeholders: 外部變量占位,一般是特征和標(biāo)簽(字典)
layers: 神經(jīng)網(wǎng)絡(luò)的layer(列表)
activations: 每個(gè)layer的輸出結(jié)果(列表)
inputs: 輸入
output: 輸出
loss: 損失
accuracy: 準(zhǔn)確率
optimizer:優(yōu)化器
opt_op:最優(yōu)化的op操作
def __init__(self, **kwargs):
allowed_kwargs = {'name', 'logging'}
for kwarg in kwargs.keys():
assert kwarg in allowed_kwargs, 'Invalid keyword argument: ' + kwarg
name = kwargs.get('name')
if not name:
name = self.__class__.__name__.lower()
self.name = name
logging = kwargs.get('logging', False)
self.logging = logging
self.vars = {}
self.placeholders = {}
self.layers = []
self.activations = []
self.inputs = None
self.outputs = None
self.loss = 0
self.accuracy = 0
self.optimizer = None
self.opt_op = None_build
(2)build方法
_build 是私有build方法,在繼承Model的具體實(shí)現(xiàn)時(shí)對layers進(jìn)行append操作,下面介紹build方法:
調(diào)用_build,所有變量設(shè)置共享空間(self.name)
構(gòu)建模型序列:給輸入,通過layer()返回輸出,又將這個(gè)輸出再次作為輸入到下一個(gè)layer()中,循環(huán)這一過程;最終,取最后一層layer的結(jié)果作為output
保存name scope下的變量到self.vars
模型效果度量:_loss方法,_accuracy方法
def _build(self):
raise NotImplementedError
def build(self):
""" Wrapper for _build() """
with tf.variable_scope(self.name):
self._build()
# Build sequential layer model
self.activations.append(self.inputs)
for layer in self.layers:
hidden = layer(self.activations[-1])
self.activations.append(hidden)
self.outputs = self.activations[-1]
# Store model variables for easy access
variables = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=self.name)
self.vars = {var.name: var for var in variables}
# Build metrics
self._loss()
self._accuracy()
self.opt_op = self.optimizer.minimize(self.loss)
(3)保存與加載方法
def save(self, sess=None):
if not sess:
raise AttributeError("TensorFlow session not provided.")
saver = tf.train.Saver(self.vars)
save_path = saver.save(sess, "tmp/%s.ckpt" % self.name)
print("Model saved in file: %s" % save_path)
def load(self, sess=None):
if not sess:
raise AttributeError("TensorFlow session not provided.")
saver = tf.train.Saver(self.vars)
save_path = "tmp/%s.ckpt" % self.name
saver.restore(sess, save_path)
print("Model restored from file: %s" % save_path)
二、數(shù)據(jù)讀取
常用的結(jié)構(gòu)化數(shù)據(jù)文件格式有csv、txt 、libsvm,本篇文章主要說明結(jié)構(gòu)化數(shù)據(jù)(csv/txt)如何在TF框架進(jìn)行高效、靈活的讀取,避免一些不合理的方式,邁出算法開發(fā)標(biāo)準(zhǔn)化、工程化的第一步。
使用pandas讀取數(shù)據(jù)
def load_data_with_pandas(filename):
# load data as DataFrame to the memory
data = pd.read_csv(filename, sep=',', header=0)
features_df = data.iloc[:, :-1]
label_df = data.iloc[:, -1]
features = features_df.astype(np.float32).values
labels = label_df.astype(np.int64).values
return features, labels
最常見的讀取數(shù)據(jù)的方式是利用pandas包將csv、txt讀取為DataFrame,一次全部放入內(nèi)存。這是一種非常低效的方式,應(yīng)該盡量避免這種讀取方法。其他第三方封裝python讀取方式的包(TFLearn等)也不建議使用,推薦使用TF框架的OP操作進(jìn)行數(shù)據(jù)讀取。
TensorFlow的數(shù)據(jù)讀取機(jī)制
一、tensorflow讀取機(jī)制圖解
首先需要思考的一個(gè)問題是,什么是數(shù)據(jù)讀取?以圖像數(shù)據(jù)為例,讀取數(shù)據(jù)的過程可以用下圖來表示:
假設(shè)我們的硬盤中有一個(gè)圖片數(shù)據(jù)集0001.jpg,0002.jpg,0003.jpg……我們只需要把它們讀取到內(nèi)存中,然后提供給GPU或是CPU進(jìn)行計(jì)算就可以了。這聽起來很容易,但事實(shí)遠(yuǎn)沒有那么簡單。事實(shí)上,我們必須要把數(shù)據(jù)先讀入后才能進(jìn)行計(jì)算,假設(shè)讀入用時(shí)0.1s,計(jì)算用時(shí)0.9s,那么就意味著每過1s,GPU都會有0.1s無事可做,這就大大降低了運(yùn)算的效率。如何解決這個(gè)問題?方法就是將讀入數(shù)據(jù)和計(jì)算分別放在兩個(gè)線程中,將數(shù)據(jù)讀入內(nèi)存的一個(gè)隊(duì)列,如下圖所示:
讀取線程源源不斷地將文件系統(tǒng)中的圖片讀入到一個(gè)內(nèi)存的隊(duì)列中,而負(fù)責(zé)計(jì)算的是另一個(gè)線程,計(jì)算需要數(shù)據(jù)時(shí),直接從內(nèi)存隊(duì)列中取就可以了。這樣就可以解決GPU因?yàn)镮O而空閑的問題!而在tensorflow中,為了方便管理,在內(nèi)存隊(duì)列前又添加了一層所謂的“文件名隊(duì)列”。
為什么要添加這一層文件名隊(duì)列?我們首先得了解機(jī)器學(xué)習(xí)中的一個(gè)概念:epoch。對于一個(gè)數(shù)據(jù)集來講,運(yùn)行一個(gè)epoch就是將這個(gè)數(shù)據(jù)集中的圖片全部計(jì)算一遍。如一個(gè)數(shù)據(jù)集中有三張圖片A.jpg、B.jpg、C.jpg,那么跑一個(gè)epoch就是指對A、B、C三張圖片都計(jì)算了一遍。兩個(gè)epoch就是指先對A、B、C各計(jì)算一遍,然后再全部計(jì)算一遍,也就是說每張圖片都計(jì)算了兩遍。
tensorflow使用文件名隊(duì)列+內(nèi)存隊(duì)列雙隊(duì)列的形式讀入文件,可以很好地管理epoch。下面我們用圖片的形式來說明這個(gè)機(jī)制的運(yùn)行方式。如下圖,還是以數(shù)據(jù)集A.jpg, B.jpg, C.jpg為例,假定我們要跑一個(gè)epoch,那么我們就在文件名隊(duì)列中把A、B、C各放入一次,并在之后標(biāo)注隊(duì)列結(jié)束。
程序運(yùn)行后,內(nèi)存隊(duì)列首先讀入A(此時(shí)A從文件名隊(duì)列中出隊(duì)):再依次讀入B和C:
此時(shí),如果再嘗試讀入,系統(tǒng)由于檢測到了“結(jié)束”,就會自動拋出一個(gè)異常(OutOfRange)。外部捕捉到這個(gè)異常后就可以結(jié)束程序了。這就是tensorflow中讀取數(shù)據(jù)的基本機(jī)制。如果我們要跑2個(gè)epoch而不是1個(gè)epoch,那只要在文件名隊(duì)列中將A、B、C依次放入兩次再標(biāo)記結(jié)束就可以了。
二、tensorflow讀取數(shù)據(jù)機(jī)制的對應(yīng)函數(shù)
如何在tensorflow中創(chuàng)建上述的兩個(gè)隊(duì)列呢?
對于文件名隊(duì)列,我們使用
tf.train.string_input_producer函數(shù)。這個(gè)函數(shù)需要傳入一個(gè)文件名list,系統(tǒng)會自動將它轉(zhuǎn)為一個(gè)文件名隊(duì)列。此外
tf.train.string_input_producer還有兩個(gè)重要的參數(shù),一個(gè)是num_epochs,它就是我們上文中提到的epoch數(shù)。另外一個(gè)就是shuffle,shuffle是指在一個(gè)epoch內(nèi)文件的順序是否被打亂。若設(shè)置shuffle=False,如下圖,每個(gè)epoch內(nèi),數(shù)據(jù)還是按照A、B、C的順序進(jìn)入文件名隊(duì)列,這個(gè)順序不會改變:
如果設(shè)置shuffle=True,那么在一個(gè)epoch內(nèi),數(shù)據(jù)的前后順序就會被打亂,如下圖所示:
在tensorflow中,內(nèi)存隊(duì)列不需要我們自己建立,我們只需要使用reader對象從文件名隊(duì)列中讀取數(shù)據(jù)就可以了,具體實(shí)現(xiàn)可以參考下面的實(shí)戰(zhàn)代碼。除了
tf.train.string_input_producer外,我們還要額外介紹一個(gè)函數(shù):tf.train.start_queue_runners。初學(xué)者會經(jīng)常在代碼中看到這個(gè)函數(shù),但往往很難理解它的用處,在這里,有了上面的鋪墊后,我們就可以解釋這個(gè)函數(shù)的作用了。在我們使用
tf.train.string_input_producer創(chuàng)建文件名隊(duì)列后,整個(gè)系統(tǒng)其實(shí)還是處于“停滯狀態(tài)”的,也就是說,我們文件名并沒有真正被加入到隊(duì)列中(如下圖所示)。此時(shí)如果我們開始計(jì)算,因?yàn)閮?nèi)存隊(duì)列中什么也沒有,計(jì)算單元就會一直等待,導(dǎo)致整個(gè)系統(tǒng)被阻塞。而使用
tf.train.start_queue_runners之后,才會啟動填充隊(duì)列的線程,這時(shí)系統(tǒng)就不再“停滯”。此后計(jì)算單元就可以拿到數(shù)據(jù)并進(jìn)行計(jì)算,整個(gè)程序也就跑起來了,這就是函數(shù)tf.train.start_queue_runners的用處。image三、實(shí)戰(zhàn)代碼
我們用一個(gè)具體的例子感受tensorflow中的數(shù)據(jù)讀取。如圖,假設(shè)我們在當(dāng)前文件夾中已經(jīng)有A.jpg、B.jpg、C.jpg三張圖片,我們希望讀取這三張圖片5個(gè)epoch并且把讀取的結(jié)果重新存到read文件夾中。
對應(yīng)的代碼如下:
# 導(dǎo)入tensorflow import tensorflow as tf # 新建一個(gè)Session with tf.Session() as sess: # 我們要讀三幅圖片A.jpg, B.jpg, C.jpg filename = ['A.jpg', 'B.jpg', 'C.jpg'] # string_input_producer會產(chǎn)生一個(gè)文件名隊(duì)列 filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5) # reader從文件名隊(duì)列中讀數(shù)據(jù)。對應(yīng)的方法是reader.read reader = tf.WholeFileReader() key, value = reader.read(filename_queue) # tf.train.string_input_producer定義了一個(gè)epoch變量,要對它進(jìn)行初始化 tf.local_variables_initializer().run() # 使用start_queue_runners之后,才會開始填充隊(duì)列 threads = tf.train.start_queue_runners(sess=sess) i = 0 while True: i += 1 # 獲取圖片數(shù)據(jù)并保存 image_data = sess.run(value) with open('read/test_%d.jpg' % i, 'wb') as f: f.write(image_data)我們這里使用
filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)建立了一個(gè)會跑5個(gè)epoch的文件名隊(duì)列。并使用reader讀取,reader每次讀取一張圖片并保存。
運(yùn)行代碼后,我們得到就可以看到read文件夾中的圖片,正好是按順序的5個(gè)epoch:
如果我們設(shè)置filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)中的shuffle=True,那么在每個(gè)epoch內(nèi)圖像就會被打亂,如圖所示:
這里只是用三張圖片舉例,實(shí)際應(yīng)用中一個(gè)數(shù)據(jù)集肯定不止3張圖片,不過涉及到的原理都是共通的。
高效的 TensorFlow 讀取方式是將數(shù)據(jù)讀取轉(zhuǎn)換成 OP,通過 session run的方式拉去數(shù)據(jù)。讀取線程源源不斷地將文件系統(tǒng)中的文件讀入到一個(gè)內(nèi)存的隊(duì)列中,而負(fù)責(zé)計(jì)算的是另一個(gè)線程,計(jì)算需要數(shù)據(jù)時(shí),直接從內(nèi)存隊(duì)列中取就可以了,這樣就可以解決GPU因?yàn)镮O而空閑的問題。同時(shí),不會一次性的preload到內(nèi)存,再大的數(shù)據(jù)量也不會超出內(nèi)存的限制。
三、TensorFlow API
1.TensorFlow 相關(guān)函數(shù)理解
- tf.nn.conv2d ————卷積
conv2d(
input,
filter,
strides,
padding,
use_cudnn_on_gpu=True,
data_format='NHWC',
name=None
)
#Example
import tensorflow as tf
a = tf.constant([1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,0,0,1,1,0,0],dtype=tf.float32,shape=[1,5,5,1])
b = tf.constant([1,0,1,0,1,0,1,0,1],dtype=tf.float32,shape=[3,3,1,1])
c = tf.nn.conv2d(a,b,strides=[1, 2, 2, 1],padding='VALID')
d = tf.nn.conv2d(a,b,strides=[1, 2, 2, 1],padding='SAME')
with tf.Session() as sess:
print ("c shape:")
print (c.shape)
print ("c value:")
print (sess.run(c))
print ("d shape:")
print (d.shape)
print ("d value:")
print (sess.run(d))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| input | 是 | tensor | 是一個(gè) 4 維的 tensor,即 [ batch, in_height, in_width, in_channels ](若 input 是圖像,[ 訓(xùn)練時(shí)一個(gè) batch 的圖片數(shù)量, 圖片高度, 圖片寬度, 圖像通道數(shù) ]) |
| filter | 是 | tensor | 是一個(gè) 4 維的 tensor,即 [ filter_height, filter_width, in_channels, out_channels ](若 input 是圖像,[ 卷積核的高度,卷積核的寬度,圖像通道數(shù),卷積核個(gè)數(shù) ]),filter 的 in_channels 必須和 input 的 in_channels 相等 |
| strides | 是 | 列表 | 長度為 4 的 list,卷積時(shí)候在 input 上每一維的步長,一般 strides[0] = strides[3] = 1 |
| padding | 是 | string | 只能為 " VALID "," SAME " 中之一,這個(gè)值決定了不同的卷積方式。VALID 丟棄方式;SAME:補(bǔ)全方式 |
| use_cudnn_on_gpu | 否 | bool | 是否使用 cudnn 加速,默認(rèn)為 true |
| data_format | 否 | string | 只能是 " NHWC ", " NCHW ",默認(rèn) " NHWC " |
| name | 否 | string | 運(yùn)算名稱 |

- tf.nn.relu ————relu激活函數(shù)
relu(
features,
name=None
)
#Example
import tensorflow as tf
a = tf.constant([1,-2,0,4,-5,6])
b = tf.nn.relu(a)
with tf.Session() as sess:
print (sess.run(b))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| features | 是 | tensor | 是以下類型float32, float64, int32, int64, uint8, int16, int8, uint16, half |
| name | 否 | string | 運(yùn)算名稱 |
- tf.nn.max_pool————池化
max_pool(
value,
ksize,
strides,
padding,
data_format='NHWC',
name=None
)
#Example
import tensorflow as tf
a = tf.constant([1,3,2,1,2,9,1,1,1,3,2,3,5,6,1,2],dtype=tf.float32,shape=[1,4,4,1])
b = tf.nn.max_pool(a,ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1],padding='VALID')
c = tf.nn.max_pool(a,ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1],padding='SAME')
with tf.Session() as sess:
print ("b shape:")
print (b.shape)
print ("b value:")
print (sess.run(b))
print ("c shape:")
print (c.shape)
print ("c value:")
print (sess.run(c))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| value | 是 | tensor | 4 維的張量,即 [ batch, height, width, channels ],數(shù)據(jù)類型為 tf.float32 |
| ksize | 是 | 列表 | 池化窗口的大小,長度為 4 的 list,一般是 [1, height, width, 1],因?yàn)椴辉?batch 和 channels 上做池化,所以第一個(gè)和最后一個(gè)維度為 1 |
| strides | 是 | 列表 | 池化窗口在每一個(gè)維度上的步長,一般 strides[0] = strides[3] = 1 |
| padding | 是 | string | 只能為 " VALID "," SAME " 中之一,這個(gè)值決定了不同的池化方式。VALID 丟棄方式;SAME:補(bǔ)全方式 |
| data_format | 否 | string | 只能是 " NHWC ", " NCHW ",默認(rèn)" NHWC " |
| name | 否 | string | 運(yùn)算名稱 |
- tf.nn.dropout————防止或減輕過擬合
dropout(
x,
keep_prob,
noise_shape=None,
seed=None,
name=None
)
#Example
import tensorflow as tf
a = tf.constant([1,2,3,4,5,6],shape=[2,3],dtype=tf.float32)
b = tf.placeholder(tf.float32)
c = tf.nn.dropout(a,b,[2,1],1)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print (sess.run(c,feed_dict={b:0.75}))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| x | 是 | tensor | 輸出元素是 x 中的元素以 keep_prob 概率除以 keep_prob,否則為 0 |
| keep_prob | 是 | scalar Tensor | dropout 的概率,一般是占位符 |
| noise_shape | 否 | tensor | 默認(rèn)情況下,每個(gè)元素是否 dropout 是相互獨(dú)立。如果指定 noise_shape,若 noise_shape[i] == shape(x)[i],該維度的元素是否 dropout 是相互獨(dú)立,若 noise_shape[i] != shape(x)[i] 該維度元素是否 dropout 不相互獨(dú)立,要么一起 dropout 要么一起保留 |
| seed | 否 | 數(shù)值 | 如果指定該值,每次 dropout 結(jié)果相同 |
| name | 否 | string | 運(yùn)算名稱 |
- tf.nn.sigmoid_cross_entropy_with_logits————先對 logits 通過 sigmoid 計(jì)算,再計(jì)算交叉熵
sigmoid_cross_entropy_with_logits(
_sentinel=None,
labels=None,
logits=None,
name=None
)
#Example
import tensorflow as tf
x = tf.constant([1,2,3,4,5,6,7],dtype=tf.float64)
y = tf.constant([1,1,1,0,0,1,0],dtype=tf.float64)
loss = tf.nn.sigmoid_cross_entropy_with_logits(labels = y,logits = x)
with tf.Session() as sess:
print (sess.run(loss))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| _sentinel | 否 | None | 沒有使用的參數(shù) |
| labels | 否 | Tensor | type, shape 與 logits相同 |
| logits | 否 | Tensor | type 是 float32 或者 float64 |
| name | 否 | string | 運(yùn)算名稱 |
- tf.truncated_normal————產(chǎn)生截?cái)嗾龖B(tài)分布隨機(jī)數(shù),取值范圍為 [ mean - 2 * stddev, mean + 2 * stddev ]
truncated_normal(
shape,
mean=0.0,
stddev=1.0,
dtype=tf.float32,
seed=None,
name=None
)
#Example
import tensorflow as tf
initial = tf.truncated_normal(shape=[3,3], mean=0, stddev=1)
print(tf.Session().run(initial))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| shape | 是 | 1 維整形張量或 array | 輸出張量的維度 |
| mean | 否 | 0 維張量或數(shù)值 | 均值 |
| stddev | 否 | 0 維張量或數(shù)值 | 標(biāo)準(zhǔn)差 |
| dtype | 否 | dtype | 輸出類型 |
| seed | 否 | 數(shù)值 | 隨機(jī)種子,若 seed 賦值,每次產(chǎn)生相同隨機(jī)數(shù) |
| name | 否 | string | 運(yùn)算名稱 |
- tf.constant————根據(jù) value 的值生成一個(gè) shape 維度的常量張量
constant(
value,
dtype=None,
shape=None,
name='Const',
verify_shape=False
)
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| value | 是 | 常量數(shù)值或者 list | 輸出張量的值 |
| dtype | 否 | dtype | 輸出張量元素類型 |
| shape | 否 | 1 維整形張量或 array | 輸出張量的維度 |
| name | 否 | string | 張量名稱 |
| verify_shape | 否 | Boolean | 檢測 shape 是否和 value 的 shape 一致,若為 Fasle,不一致時(shí),會用最后一個(gè)元素將 shape 補(bǔ)全 |
- tf.placeholder————是一種占位符,在執(zhí)行時(shí)候需要為其提供數(shù)據(jù)
placeholder(
dtype,
shape=None,
name=None
)
#Example
import tensorflow as tf
import numpy as np
x = tf.placeholder(tf.float32,[None,3])
y = tf.matmul(x,x)
with tf.Session() as sess:
rand_array = np.random.rand(3,3)
print(sess.run(y,feed_dict={x:rand_array}))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| dtype | 是 | dtype | 占位符數(shù)據(jù)類型 |
| shape | 否 | 1 維整形張量或 array | 占位符維度 |
| name | 否 | string | 占位符名稱 |
- tf.nn.bias_add————將偏差項(xiàng) bias 加到 value 上面,可以看做是 tf.add 的一個(gè)特例,其中 bias 必須是一維的,并且維度和 value 的最后一維相同,數(shù)據(jù)類型必須和 value 相同。
bias_add(
value,
bias,
data_format=None,
name=None
)
#Example
import tensorflow as tf
import numpy as np
a = tf.constant([[1.0, 2.0],[1.0, 2.0],[1.0, 2.0]])
b = tf.constant([2.0,1.0])
c = tf.constant([1.0])
sess = tf.Session()
print (sess.run(tf.nn.bias_add(a, b)))
#print (sess.run(tf.nn.bias_add(a,c))) error
print ("##################################")
print (sess.run(tf.add(a, b)))
print ("##################################")
print (sess.run(tf.add(a, c)))
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| value | 是 | 張量 | 數(shù)據(jù)類型為 float, double, int64, int32, uint8, int16, int8, complex64, or complex128 |
| bias | 是 | 一維張量 | 維度必須和 value 最后一維維度相等 |
| data_format | 否 | string | 數(shù)據(jù)格式,支持 ' NHWC ' 和 ' NCHW ' |
| name | 否 | string | 運(yùn)算名稱 |
- tf.reduce_mean————計(jì)算張量 input_tensor 平均值
reduce_mean(
input_tensor,
axis=None,
keep_dims=False,
name=None,
reduction_indices=None
)
#Example
import tensorflow as tf
import numpy as np
initial = [[1.,1.],[2.,2.]]
x = tf.Variable(initial,dtype=tf.float32)
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
print(sess.run(tf.reduce_mean(x)))
print(sess.run(tf.reduce_mean(x,0))) #Column
print(sess.run(tf.reduce_mean(x,1))) #row
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| input_tensor | 是 | 張量 輸入待求平均值的張量 | |
| axis | 否 | None、0、1 | None:全局求平均值;0:求每一列平均值;1:求每一行平均值 |
| keep_dims | 否 | Boolean | 保留原來的維度(例如不會從二維矩陣降為一維向量) |
| name | 否 | string | 運(yùn)算名稱 |
| reduction_indices | 否 | None | 和 axis 等價(jià),被棄用 |
- tf.squared_difference————計(jì)算張量 x、y 對應(yīng)元素差平方
squared_difference(
x,
y,
name=None
)
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| x | 是 | 張量 | 是 half, float32, float64, int32, int64, complex64, complex128 其中一種類型 |
| y | 是 | 張量 | 是 half, float32, float64, int32, int64, complex64, complex128 其中一種類型 |
| name | 否 | string | 運(yùn)算名稱 |
- tf.square————計(jì)算張量對應(yīng)元素平方
square(
x,
name=None
)
| 參數(shù)名 | 必選 | 類型 | 說明 |
|---|---|---|---|
| x | 是 | 張量 | 是half, float32, float64, int32, int64, complex64, complex128 其中一種類型 |
| name | 否 | string | 運(yùn)算名稱 |
2.Tensorflow相關(guān)類理解
- tf.Variable————維護(hù)圖在執(zhí)行過程中的狀態(tài)信息,例如神經(jīng)網(wǎng)絡(luò)權(quán)重值的變化
__init__(
initial_value=None,
trainable=True,
collections=None,
validate_shape=True,
caching_device=None,
name=None,
variable_def=None,
dtype=None,
expected_shape=None,
import_scope=None
)
| 參數(shù)名 | 類型 | 說明 |
|---|---|---|
| initial_value | 張量 | Variable 類的初始值,這個(gè)變量必須指定 shape 信息,否則后面 validate_shape 需設(shè)為 False |
| trainable | Boolean | 是否把變量添加到 collection GraphKeys.TRAINABLE_VARIABLES 中(collection 是一種全局存儲,不受變量名生存空間影響,一處保存,到處可?。?/td> |
| collections | Graph collections | 全局存儲,默認(rèn)是 GraphKeys.GLOBAL_VARIABLES |
| validate_shape | Boolean | 是否允許被未知維度的 initial_value 初始化 |
| caching_device | string | 指明哪個(gè) device 用來緩存變量 |
| name | string | 變量名 |
| dtype | dtype | 如果被設(shè)置,初始化的值就會按照這個(gè)類型初始化 |
| expected_shape | TensorShape | 要是設(shè)置了,那么初始的值會是這種維度 |
四、LeNet網(wǎng)絡(luò)的構(gòu)建
#模型訓(xùn)練
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
#打開數(shù)據(jù)集
path='C:/Users/86769/.keras/datasets/mnist.npz'
f = np.load(path)
x_train, y_train = f['x_train'], f['y_train']
x_test, y_test = f['x_test'], f['y_test']
f.close()
#格式化
x_train = np.pad(x_train,((0,0),(2,2),(2,2)),'constant',constant_values=0)
x_train = x_train.astype('float32')
x_train /=255
x_train=x_train.reshape(x_train.shape[0],32,32,1)
x_test = np.pad(x_test,((0,0),(2,2),(2,2)),'constant',constant_values=0)
x_test = x_test.astype('float32')
x_test /=255
x_test=x_test.reshape(x_test.shape[0],32,32,1)
#創(chuàng)建模型
model=tf.keras.models.Sequential([
tf.keras.layers.Conv2D(filters=6,kernel_size=(5,5),padding='valid',activation=tf.nn.relu,input_shape=(32,32,1)),
tf.keras.layers.AveragePooling2D(pool_size=(2,2),strides=(2,2),padding='same'),
tf.keras.layers.Conv2D(filters=16,kernel_size=(5,5),padding='valid',activation=tf.nn.relu),
tf.keras.layers.AveragePooling2D(pool_size=(2,2),strides=(2,2),padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(units=120,activation=tf.nn.relu),
# tf.keras.layers.Conv2D(filters=120,kernel_size=(5,5),strides=(1,1),activation='tanh',padding='valid'),
# tf.keras.layers.Flatten(),
tf.keras.layers.Dense(units=84,activation=tf.nn.relu),
tf.keras.layers.Dense(units=10,activation=tf.nn.softmax)
])
model.summary()
#超參數(shù)配置
num_epochs=10 #訓(xùn)練輪數(shù)
batch_size=64 #批大小
learning_rate=0.001 #學(xué)習(xí)率
#優(yōu)化器
adam_optimizer=tf.keras.optimizers.Adam(learning_rate)
model.compile(optimizer=adam_optimizer,
loss=tf.keras.losses.sparse_categorical_crossentropy,metrics=['accuracy'])
import datetime
start_time=datetime.datetime.now()
model.fit(x=x_train,
y=y_train,
batch_size=batch_size,
epochs=num_epochs)
end_time=datetime.datetime.now()
time_cost=end_time-start_time
print("time_cost = ",time_cost) #CPU time cost: 5min, GPU time cost: less than 1min
#模型保存
model.save('E:/ML/LeNet_Model.hui')
#評估指標(biāo)
print(model.evaluate(x_test, y_test)) #loss value & metrics value
#預(yù)測
image_index=4444
print(x_test[image_index].shape)
plt.imshow(x_test[image_index].reshape(32,32),cmap='Greys')
pred = model.predict((x_test[image_index].reshape(1,32,32,1)))
print(pred.argmax())
神經(jīng)網(wǎng)絡(luò)中的幾個(gè)重要概念
梯度下降:梯度下降是一個(gè)在機(jī)器學(xué)習(xí)中用于尋找較佳結(jié)果(曲線的最小值)的迭代優(yōu)化算法。梯度的含義是斜率或者斜坡的傾斜度。下降的含義是代價(jià)函數(shù)的下降。
?算法是迭代的,意思是需要多次使用算法獲取結(jié)果,以得到最優(yōu)化結(jié)果。梯度下降的迭代性質(zhì)能使欠擬合演變成獲得對數(shù)據(jù)的較佳擬合。

?梯度下降中有一個(gè)稱為學(xué)習(xí)率的參量。如上圖左所示,剛開始學(xué)習(xí)率較大,因此下降步長更大。隨著點(diǎn)的下降,學(xué)習(xí)率變得越來越小,從而下降步長也變小。同時(shí),代價(jià)函數(shù)也在減小,或者說代價(jià)在減小,有時(shí)候也稱為損失函數(shù)或者損失,兩者是一樣的。(損失/代價(jià)的減小是一個(gè)概念)。
?只有在數(shù)據(jù)很龐大的時(shí)候(在機(jī)器學(xué)習(xí)中,數(shù)據(jù)一般情況下都會很大),我們才需要使用epochs,batch size,iteration這些術(shù)語,在這種情況下,一次性將數(shù)據(jù)輸入計(jì)算機(jī)是不可能的。因此,為了解決這個(gè)問題,我們需要把數(shù)據(jù)分成小塊,一塊一塊的傳遞給計(jì)算機(jī),在每一步的末端更新神經(jīng)網(wǎng)絡(luò)的權(quán)重,擬合給定的數(shù)據(jù)。
(1)batchsize:批大小。在深度學(xué)習(xí)中,一般采用SGD訓(xùn)練,即每次訓(xùn)練在訓(xùn)練集中取batchsize個(gè)樣本訓(xùn)練;
(2)iteration:1個(gè)iteration等于使用batchsize個(gè)樣本訓(xùn)練一次;
(3)epoch:1個(gè)epoch等于使用訓(xùn)練集中的全部樣本訓(xùn)練一次;
- batchsize:批大?。ㄅ叽纾?/strong>
批大小將決定我們一次訓(xùn)練的樣本數(shù)目。
batch_size將影響到模型的優(yōu)化程度和速度。batchsize的正確選擇是為了在內(nèi)存效率和內(nèi)存容量之間尋找最佳平衡。
Batch_Size的取值:
BatchSize
全批次(藍(lán)色)
如果數(shù)據(jù)集比較小,我們就采用全數(shù)據(jù)集。全數(shù)據(jù)集確定的方向能夠更好的代表樣本總體,從而更準(zhǔn)確的朝向極值所在的方向。
注:對于大的數(shù)據(jù)集,我們不能使用全批次,因?yàn)闀玫礁畹慕Y(jié)果。迷你批次(綠色)
選擇一個(gè)適中的Batch_Size值。就是說我們選定一個(gè)batch的大小后,將會以batch的大小將數(shù)據(jù)輸入深度學(xué)習(xí)的網(wǎng)絡(luò)中,然后計(jì)算這個(gè)batch的所有樣本的平均損失,即代價(jià)函數(shù)是所有樣本的平均。隨機(jī)(Batch_Size等于1的情況)(紅色)
每次修正方向以各自樣本的梯度方向修正,橫沖直撞各自為政,難以達(dá)到收斂。適當(dāng)?shù)脑黾覤atch_Size的優(yōu)點(diǎn):
- 1.通過并行化提高內(nèi)存利用率。
- 2.單次epoch的迭代次數(shù)減少,提高運(yùn)行速度。(單次epoch=(全部訓(xùn)練樣本/batchsize)/iteration=1)
- 3.適當(dāng)?shù)脑黾覤atch_Size,梯度下降方向準(zhǔn)確度增加,訓(xùn)練震動的幅度減小。(看上圖便可知曉)
經(jīng)驗(yàn)總結(jié):
相對于正常數(shù)據(jù)集,如果Batch_Size過小,訓(xùn)練數(shù)據(jù)就會非常難收斂,從而導(dǎo)致underfitting。
增大Batch_Size,相對處理速度加快,所需內(nèi)存容量增加(epoch的次數(shù)需要增加以達(dá)到最好的結(jié)果)
這里我們發(fā)現(xiàn)上面兩個(gè)矛盾的問題,因?yàn)楫?dāng)epoch增加以后同樣也會導(dǎo)致耗時(shí)增加從而速度下降。因此我們需要尋找最好的Batch_Size。
iteration:迭代
迭代是重復(fù)反饋的動作,神經(jīng)網(wǎng)絡(luò)中我們希望通過迭代進(jìn)行多次的訓(xùn)練以達(dá)到所需的目標(biāo)或結(jié)果。
每一次迭代得到的結(jié)果都會被作為下一次迭代的初始值。
一個(gè)迭代=一個(gè)正向通過+一個(gè)反向通過
Epoch
當(dāng)一個(gè)完整的數(shù)據(jù)集通過了神經(jīng)網(wǎng)絡(luò)一次并且返回了一次,這個(gè)過程稱為一次epoch。然而,當(dāng)一個(gè)epoch對于計(jì)算機(jī)而言太龐大的時(shí)候,就需要把它分成多個(gè)小塊。
為什么要使用多于一個(gè)epoch?
在神經(jīng)網(wǎng)絡(luò)中傳遞完整的數(shù)據(jù)集一次是不夠的,而且我們需要將完整的數(shù)據(jù)集在同樣的神經(jīng)網(wǎng)絡(luò)中傳遞多次。但請記住,我們使用的是有限的數(shù)據(jù)集,并且我們使用一個(gè)迭代過程即梯度下降來優(yōu)化學(xué)習(xí)過程。如下圖所示。因此僅僅更新一次或者說使用一個(gè)epoch是不夠的。
Epoch Influence
隨著epoch數(shù)量增加,神經(jīng)網(wǎng)絡(luò)中的權(quán)重的更新次數(shù)也在增加,曲線從欠擬合變得過擬合。














