卷積神經網路中loss值為nan的問題(已解決)
阿新 • • 發佈:2018-12-18
卷積神經網路中loss值為nan的問題(已經解決了,請看最後的說明)
最近一直在學習AI方面的東西。想自己搞一個類似MINST的東東,用搞基神經網路 ,樣本用自制的樣本……然鵝理想是骨感的,現實是永遠吃不上飯的。經過與各種錯誤輪戰後,出現了更大的錯誤:loss為nan
先上程式碼吧,比較亂,建議摘了眼鏡看: 首先是整體程式碼:
import os
import glob
from skimage import io,transform
import numpy as np
import cv2
import matplotlib.pyplot as plt
import tensorflow as tf
import time
# path = "E:\\study\\MINST-PLUS\\data"
# data_size = [28,28,3]
def read_img (path, data_size):
imgs = []
labels = []
cate = []
for x in os.listdir(path): # 讀取資料夾裡所有資料夾的路徑,賦值到cate列表
if (os.path.isdir(path+'\\'+x)):
cate.append(path+'\\'+x)
# cate=[path+x for x in os.listdir(path) if os.path.isdir(path+x)] 大神都這樣寫,但是不好理解,有些太perl
for idx,folder in enumerate(cate): #給資料夾排序號,0是0資料夾,1是1資料夾...
for im in glob.glob(folder+'/*.jpg'): #遍歷資料夾內的*.jpg檔案(路徑)
# print(im)
img = cv2.imread(im) #讀取jpg檔案
# img = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) #把圖片轉換為黑白(彩色是3維,黑白是2維)
img = cv2.resize(img,(data_size[0 ],data_size[1]),interpolation=cv2.INTER_CUBIC)
# 根據要求調整圖片大小
# img = 255 - img # 以下為圖片的處理 ,先減去一個255
# for row in range(img.shape[0]):
# for col in range(img.shape[1]):
# if img[row][col] < 120.0: #把灰度120以下的化為0(降噪)
# img[row][col] = 0
# img = img / 255 # 把各個直降到1-0之間的小數
imgs.append(img) # 追加到陣列imgs
labels.append(idx) # 追加到陣列labels
# plt.imshow(img,'gray')
# plt.show()
# print(np.shape(labels))
# print(np.shape(img)) #注意img是單張圖片是32*32的二維陣列
# print(np.shape(imgs)) #但是imgs是:第幾張圖片*32*32 的三維陣列,現在我有160張圖片,所以是 160*28*28
# print (labels) #標籤的順序也是依照圖片的順序匹配的,也就是:imgs[0] 這個圖片的標籤是labels[0]
# print (len(labels)) #相對的labels的總數,到現在為止是160個標籤
np.asarray(imgs,np.float32),np.asarray(labels,np.float32) # 將陣列轉化為矩陣
return np.asarray(imgs,np.float32),np.asarray(labels,np.float32)
# 以上我把圖片轉換為灰度圖的程式碼註釋掉了,現在是直接讀取的圖片。
def data_split_flow (data,label,ratio):# data是讀取的圖片集合,label是圖片的標籤集合,ratio是你想要(百分之)多少資料用於培訓.
num_example = data.shape[0] # 這個data是讀取圖片的合計,其中第一維就是圖片的序號,也就是圖片的總量
arr = np.arange(num_example) # np.arange(起始值,終止值,步長) 與arange(起始值,終止值,步長) 不同之處是np.arange的引數可以是小數,這裡應該是np.arange(28)
np.random.shuffle(arr) #隨機打亂順序函式,多維矩陣中,只對第一維做打亂順序操作。也就是np.arange(28)中的順序被隨機打亂
# print (type(arr))
# print (type(data))
# print (data.shape)
data = data[arr] # 因為arr現在是一維的隨機化的np矩陣,用它可以覆蓋掉原資料的第一維,也就是重新給data排序
label = label[arr] # 同理,也同樣使label標籤隨機化,這兩次隨機化的引數arr是相同的,也就是隨機後的資料和標籤是可以對上號的。
# print (data.shape)
s = np.int(num_example*ratio) # 圖片總數*想要取走百分之多少,並且取整,然後賦予s
x_train = data[:s] #以下是把圖片分為“訓練用圖片”“訓練用圖片的標籤”,“驗證用圖片”“驗證用圖片的標籤”。其中[:s]或[s:]是列表的切片,表示由開始到s,或由s到最後。
y_train = label[:s]
x_val = data[s:]
y_val = label[s:]
return x_train,y_train,x_val,y_val
#c,d,e,f = data_split_flow(a,b,0.8)
def cnn_fc (input_tensor,train,regularizer):
with tf.variable_scope('layer1-conv1'): # 開啟一個聯絡上下文的名稱空間,空間名是layer1-conv1,在tf.get_variable可以順利呼叫
conv1_weights = tf.get_variable('weight',[5,5,3,6],initializer = tf.truncated_normal_initializer(stddev = 0.1))
#上面一行命令是生成卷積核:是一個tansor型別,具體含義是[卷積核的高度,卷積核的寬度,影象通道數,卷積核個數],要求型別與引數input相同,有一個地方需要注意,第三維in_channels,就是引數input的第四維
# tf.truncated_normal_initializer:從截斷的正態分佈中輸出隨機值。這是神經網路權重和過濾器的推薦初始值。
# mean:一個python標量或一個標量張量。要生成的隨機值的均值。
# stddev:一個python標量或一個標量張量。要生成的隨機值的標準偏差。
# seed:一個Python整數。用於建立隨機種子。檢視 tf.set_random_seed 行為。
# dtype:資料型別。只支援浮點型別。
conv1_biases = tf.get_variable("bias",[6],initializer = tf.constant_initializer(0.0))
conv1 = tf.nn.conv2d(input_tensor,conv1_weights,strides=[1,1,1,1],padding="SAME")
# 除去name引數用以指定該操作的name,與方法有關的一共五個引數:
#第一個引數input:指需要做卷積的輸入影象,它要求是一個Tensor,具有[batch, in_height, in_width, in_channels]這樣的shape,
#具體含義是[訓練時一個batch的圖片數量, 圖片高度, 圖片寬度, 影象通道數],注意這是一個4維的Tensor,要求型別為float32和float64其中之一
#第二個引數filter:相當於CNN中的卷積核,它要求是一個Tensor,具有[filter_height, filter_width, in_channels, out_channels]這樣的shape,
#具體含義是[卷積核的高度,卷積核的寬度,影象通道數,卷積核個數],要求型別與引數input相同,有一個地方需要注意,第三維in_channels,就是引數input的第四維
#第三個引數strides:卷積時在影象每一維的步長,這是一個一維的向量,長度4
#第四個引數padding:string型別的量,只能是"SAME","VALID"其中之一,這個值決定了不同的卷積方式(後面會介紹)
#第五個引數:use_cudnn_on_gpu:bool型別,是否使用cudnn加速,預設為true#
#結果返回一個Tensor,這個輸出,就是我們常說的feature map特徵圖,shape仍然是[batch, height, width, channels]這種形式。
relu1 = tf.nn.relu(tf.nn.bias_add(conv1,conv1_biases))
# 啟用函式,非最大值置零
# 這個函式的作用是計算啟用函式 relu,即 max(features, 0)。即將矩陣中每行的非最大值置0。
with tf.name_scope("layer2-pool1"):
pool1 = tf.nn.max_pool(relu1,ksize = [1,2,2,1], strides=[1,2,2,1],padding="VALID")
#tf.nn.max_pool(value, ksize, strides, padding, name=None)
#引數是四個,和卷積很類似:
#第一個引數value:需要池化的輸入,一般池化層接在卷積層後面,所以輸入通常是feature map,依然是[batch, height, width, channels]這樣的shape
#第二個引數ksize:池化視窗的大小,取一個四維向量,一般是[1, height, width, 1],因為我們不想在batch和channels上做池化,所以這兩個維度設為了1
#第三個引數strides:和卷積類似,視窗在每一個維度上滑動的步長,一般也是[1, stride,stride, 1]
#第四個引數padding:和卷積類似,可以取'VALID' 或者'SAME'
#返回一個Tensor,型別不變,shape仍然是[batch, height, width, channels]這種形式
with tf.variable_scope("layer3-conv2"):
conv2_weights = tf.get_variable("weight",[5,5,6,16],initializer=tf.truncated_normal_initializer(stddev=0.1))# [5,5,32,64] 5表示本次卷積核高寬,32表示經過上一層32個卷積核的卷積,我們有了32張特徵圖,64表明本次會有64個卷積核卷積
conv2_biases = tf.get_variable("bias", [16], initializer=tf.constant_initializer(0.0))
conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))
with tf.name_scope("layer4-pool2"):
pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')
nodes = 8*8*16
reshaped = tf.reshape(pool2,[-1,nodes]) # 其中pool2是連結上一層,我把pool4和pool3的卷積核池化層刪除了,卷的太多都要成渣渣了。
# tf.reshape(tensor(矩陣),shape(維度),name=None)
# 改變一個矩陣的維度,可以從多維變到一維,也可以從一維變到多維
# 其中,-1引數表示不確定,可由函式自己計算出來,原矩陣/一個維度=另一個維度
with tf.variable_scope('layer9-fc1'):
fc1_weights = tf.get_variable("weight", [nodes, 1024],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc1_weights))
# tf.add_to_collection:把變數放入一個集合,把很多變數變成一個列表
#在深度學習中,通常用這幾個函式存放不同層中的權值和偏置引數,
#也就是把所有可學習引數利用tf.contrib.layers.l2_regularizer(regular_num)(w)得到norm後,都放到’regular’的列表中作為正則項,
#然後使用tf.add_n函式將他們和原本的loss相加,得到含有正則的loss。
fc1_biases = tf.get_variable("bias", [1024], initializer=tf.constant_initializer(0.1))
fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases) # MCP模型
if train: fc1 = tf.nn.dropout(fc1, 0.5) #tf.nn.dropout是TensorFlow裡面為了防止或減輕過擬合而使用的函式,它一般用在全連線層。
# tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None,name=None)
# 上面方法中常用的是前兩個引數:
# 第一個引數x:指輸入的資料。
# 第二個引數keep_prob: 設定神經元被選中的概率,在初始化時keep_prob是一個佔位符, keep_prob = tf.placeholder(tf.float32) 。
# tensorflow在run時設定keep_prob具體的值,例如keep_prob: 0.5
# 第五個引數name:指定該操作的名字。
with tf.variable_scope('layer10-fc2'):
fc2_weights = tf.get_variable("weight", [1024, 512],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc2_weights))
fc2_biases = tf.get_variable("bias", [512], initializer=tf.constant_initializer(0.1))
fc2 = tf.nn.relu(tf.matmul(fc1, fc2_weights) + fc2_biases)
if train: fc2 = tf.nn.dropout(fc2, 0.5)
with tf.variable_scope('layer11-fc3'):
fc3_weights = tf.get_variable("weight", [512, 5],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc3_weights))
fc3_biases = tf.get_variable("bias", [5], initializer=tf.constant_initializer(0.1))
logit = tf.matmul(fc2, fc3_weights) + fc3_biases
# variable_summaries(fc3_weights)
# variable_summaries(fc3_biases)
return logit
def minibatches(inputs=None, targets=None, batch_size=None, shuffle=False): #四個引數是:訓練資料,測試資料,使用者輸入的每批訓練的資料數量,shuffle是洗牌的意思,這裡表示是否開始隨機。
assert len(inputs) == len(targets) #assert斷言機制,如果後面的表示式為真,則直接丟擲異常。在這裡的意思,大概就是:樣本和標籤數量要對上
if shuffle:
indices = np.arange(len(inputs)) #生成一個np.arange可迭代長度是len(訓練資料),也就是訓練資料第一維資料的數量(就是訓練資料的數量,訓練圖片的數量)。
np.random.shuffle(indices) #np.random.shuffle打亂arange中的順序,使其隨機循序化,如果是陣列,只打亂第一維。
for start_idx in range(0, len(inputs) - batch_size + 1, batch_size): # 這個range(初始值為0,終止值為[訓練圖片數-每批訓練圖片數+1],步長是[每批訓練圖片數]):例(0[起始值],80[訓練圖片數]-20[每批訓練圖片數],20[每批訓練圖片數]),也就是(0,60,20)當迴圈到60時,會加20到達80的訓練樣本.
if shuffle:
excerpt = indices[start_idx:start_idx + batch_size] # 如果shuffle為真,將indices列表,切片(一批)賦值給excerpt
else:
excerpt = slice(start_idx, start_idx + batch_size) # 如果shuffle為假,將slice()函式(切片函式),例項化,初始值為start_idx,結束為止為start_idx + batch_size(也就是根據上一批起始,算出本批結束的位置.),間距為預設.
yield inputs[excerpt], targets[excerpt]
#yield常見用法:該關鍵字用於函式中會把函式包裝為generator。然後可以對該generator進行迭代: for x in fun(param).
#按照我的理解,可以把yield的功效理解為暫停和播放。
#在一個函式中,程式執行到yield語句的時候,程式暫停,返回yield後面表示式的值,在下一次呼叫的時候,從yield語句暫停的地方繼續執行,如此迴圈,直到函式執行完。
#此處,就是返回每次迴圈中 從inputs和targets列表中,擷取的 經過上面slice()切片函式定義過的 資料.
#(最後的shuffle變數,決定了樣本是否隨機化)
# 設定超參
path = "E:\\study\\MINST-PLUS\\data"
data_size = [32,32,3]
data_ratio = 0.6
is_train = False
epoch = 5
batch_size = 4
# 樣本和標籤的讀入與分類
data,label = read_img(path,data_size)
x_train,y_train,x_val,y_val = data_split_flow(data,label,data_ratio)
# 為資料與標籤設立兩個存放空間
x = tf.placeholder(tf.float32,shape=[None,data_size[0],data_size[1],data_size[2]],name = 'x') # 如果是灰度圖,通道應該是1,彩色是3
y_ = tf.placeholder(tf.int32,shape=[None,],name = 'y_')
# 定義規則化方法,並計算網路啟用值
regularizer = tf.contrib.layers.l2_regularizer(0.0001) # 過擬合與正則化(regularizer),這個regularizer就是inference函式的最後一個引數。
#兩種思想都是希望限制權重的大小,使得模型不能擬合訓練資料中的隨機噪點。(兩種思想,就是兩個公式,因為是圖,就沒貼出來)
#兩種方式在TensorFlow中的提供的函式為:
#tf.contrib.layers.l1_regularizer(scale, scope=None) 其中scale為權值(這個權值會乘以w的值,MCP的內個w,江湖傳聞w和過擬合值有說不清的關係)
#tf.contrib.layers.l2_regularizer(scale, scope=None)
logits = cnn_fc(x,is_train,regularizer) #x是輸入的影象的tansor,logits是經過卷積、池化、全連線處理處理過的資料
# b = tf.constant(value=1,dtype=tf.float32) # constant(值、列表 , 陣列格式)根據值、列表,生成一個數組,格式為“陣列格式”
# logits_eval = tf.multiply(logits,b,name='logits_eval') # 額,不知道這是計算啥
# 計算誤差與準確率,並寫入日誌 (我沒有日誌,呵呵)
loss = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=logits) #計算logits 和 labels 之間的稀疏softmax 交叉熵 這個是計算誤差率
train_op = tf.train.AdamOptimizer(learning_rate=0.01).minimize(loss) # tf.train.AdamOptimizer 優化器中的梯度優化函式,
# 作用是依據learning_rate步長,來最小化loss誤差率。
correct_prediction = tf.equal(tf.cast(tf.argmax(logits,1),tf.int32), y_)
#tf.argmax(vector, 1):返回的是vector中的最大值的索引號,如果vector是一個向量,那就返回一個值,如果是一個矩陣,那就返回一個向量,
#這個向量的每一個維度都是相對應矩陣行的最大值元素的索引號。
# tf.cast(x, dtype, name=None)
# 此函式是型別轉換函式
# 引數
# x:輸入
# dtype:轉換目標型別
# name:名稱
# 返回:Tensor
# tf.equal(A, B)是對比這兩個矩陣或者向量的相等的元素,如果是相等的那就返回True,反正返回False,
# 返回的值的矩陣維度和A是一樣的,返回的也是一個矩陣、向量、列表,裡面都是true和false。
#這一行的意思,大概是,通過以上三個函式,對比處理後的logits值和labels值,然後得出一個判斷表單
acc= tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) #這個......大概是在計算準確率
# 求平均值tf.reduce_mean(input_tensor, reduction_indices=None, keep_dims=False, name=None)
# 引數1--input_tensor:待求值的tensor。
# 引數2--reduction_indices:在哪一維上求解。
# 引數(3)(4)可忽略
# tf.summary.scalar('accuracy', acc)
# 建立儲存點,並進入計算圖流程 還有限制gpu,我的電腦沒有這句話就各種死
saver=tf.train.Saver()
sess=tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)) #限制gpu記憶體
sess.run(tf.global_variables_initializer()) # 全世界get_variable嗨起來
# 定義日誌彙總操作,初始化訓練日誌與驗證日誌的writer
# 我木有日誌,呵呵
# 開始訓練:第一個for迴圈,是總體訓練的次數。第一個for子迴圈是訓練樣本迴圈,當其結束也就是訓練樣本被整體遍歷了一次。第二個也一樣,不過是驗證樣本。
for epoch in range(epoch): # 訓練多少遍,FLAGS.epoch是使用者輸入的,比如是5,也就是把樣本遍歷5遍
start_time = time.time() # 開始計時
### 單次訓練部分 此處for迴圈結束之日,就是訓練樣本遍歷了一遍之時......
train_loss, train_acc, n_batch = 0, 0, 0 # 先定義下訓練誤差,訓練識別率,訓練批次
for x_train_a, y_train_a in minibatches(x_train, y_train,batch_size, shuffle=True):
#遍歷minibatches函式,因為這個函式中有yield關鍵字,每次for,會獲取到不同的批次,直到訓練樣本集合身體被掏空。注意,這裡shuffle為True,樣本是隨機的。
_, err, ac = sess.run([train_op, loss, acc], feed_dict={x: x_train_a, y_: y_train_a}) #向sess.run中喂資料,
# 其中merged是train_summary計算圖;train_op是梯度優化方法,err接收的loss是誤差率;ac接收的acc是準確率。後面的x和y_就是每批的資料和標籤。
train_loss += err; train_acc += ac; n_batch += 1 #統計誤差率、準確率、批次
print(" train loss: %f" % (np.sum(train_loss)/ n_batch))
print(" train acc: %f" % (np.sum(train_acc)/ n_batch))
### 單次驗證部分 具體和上面雷同,下面是計算的測試資料,不用梯度優化了
val_loss, val_acc, n_batch = 0, 0, 0
for x_val_a, y_val_a in minibatches(x_val, y_val,batch_size, shuffle=False):
err, ac = sess.run([loss, acc], feed_dict={x: x_val_a, y_: y_val_a})
val_loss += err; val_acc += ac; n_batch += 1
print(" validation loss: %f" % (np.sum(val_loss)/ n_batch))
print(" validation acc: %f" % (np.sum(val_acc)/ n_batch))
print('-------------------------------------------------------')
## 儲存模型
# saver.save(sess,FLAGS.model_dir)
sess.close()
#### 下面是測試輸出logits值和loss值的程式碼:
import os
import glob
from skimage import io,transform
import numpy as np
import cv2
import matplotlib.pyplot as plt
import tensorflow as tf
import time
def read_img (path, data_size):
imgs = []
labels = []
cate = []
for x in os.listdir(path): # 讀取資料夾裡所有資料夾的路徑,賦值到cate列表
if (os.path.isdir(path+'\\'+x)):
cate.append(path+'\\'+x)
# cate=[path+x for x in os.listdir(path) if os.path.isdir(path+x)] 大神都這樣寫,但是不好理解,有些太perl
for idx,folder in enumerate(cate): #給資料夾排序號,0是0資料夾,1是1資料夾...
for im in glob.glob(folder+'/*.jpg'): #遍歷資料夾內的*.jpg檔案(路徑)
# print(im)
img = cv2.imread(im) #讀取jpg檔案
# img = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) #把圖片轉換為黑白(彩色是3維,黑白是2維)
img = cv2.resize(img,(data_size[0],data_size[1]),interpolation=cv2.INTER_CUBIC)
# 根據要求調整圖片大小
# img = 255 - img # 以下為圖片的處理 ,先減去一個255
# for row in range(img.shape[0]):
# for col in range(img.shape[1]):
# if img[row][col] < 120.0: #把灰度120以下的化為0(降噪)
# img[row][col] = 0
# img = img / 255 # 把各個直降到1-0之間的小數
imgs.append(img) # 追加到陣列imgs
labels.append(idx) # 追加到陣列labels
# plt.imshow(img,'gray')
# plt.show()
# print(np.shape(labels))
# print(np.shape(img)) #注意img是單張圖片是32*32的二維陣列
# print(np.shape(imgs)) #但是imgs是:第幾張圖片*32*32 的三維陣列,現在我有54張圖片,所以是 54*32*32
# print (labels) #標籤的順序也是依照圖片的順序匹配的,也就是:imgs[0] 這個圖片的標籤是labels[0]
# print (len(labels)) #相對的labels的總數,到現在為止是160個標籤
# 因為在給卷積函式資料時是[每批的樣本數量,樣本長,樣本寬,樣本通道數],如果是灰度圖,是沒有通道數的,需要增加一維,此處我想做彩色的,所以把之前的彩色轉換為灰度,註釋掉了。
np.asarray(imgs,np.float32),np.asarray(labels,np.float32) # 將陣列轉化為矩陣
return np.asarray(imgs,np.float32),np.asarray(labels,np.float32)
def data_split_flow (data,label,ratio):# data是讀取的圖片集合,label是圖片的標籤集合,ratio是你想要(百分之)多少資料用於培訓.
num_example = data.shape[0] # 這個data是讀取圖片的合計,其中第一維就是圖片的序號,也就是圖片的總量
arr = np.arange(num_example) # np.arange(起始值,終止值,步長) 與arange(起始值,終止值,步長) 不同之處是np.arange的引數可以是小數,這裡應該是np.arange(28)
np.random.shuffle(arr) #隨機打亂順序函式,多維矩陣中,只對第一維做打亂順序操作。也就是np.arange(28)中的順序被隨機打亂
# print (type(arr))
# print (type(data))
# print (data.shape)
data = data[arr] # 因為arr現在是一維的隨機化的np矩陣,用它可以覆蓋掉原資料的第一維,也就是重新給data排序
label = label[arr] # 同理,也同樣使label標籤隨機化,這兩次隨機化的引數arr是相同的,也就是隨機後的資料和標籤是可以對上號的。
# print (data.shape)
s = np.int(num_example*ratio) # 圖片總數*想要取走百分之多少,並且取整,然後賦予s
x_train = data[:s] #以下是把圖片分為“訓練用圖片”“訓練用圖片的標籤”,“驗證用圖片”“驗證用圖片的標籤”。其中[:s]或[s:]是列表的切片,表示由開始到s,或由s到最後。
y_train = label[:s]
x_val = data[s:]
y_val = label[s:]
return x_train,y_train,x_val,y_val
#c,d,e,f = data_split_flow(a,b,0.8)
def minibatches(inputs=None, targets=None, batch_size=None, shuffle=False): #四個引數是:訓練資料,測試資料,使用者輸入的每批訓練的資料數量,shuffle是洗牌的意思,這裡表示是否開始隨機。
assert len(inputs) == len(targets) #assert斷言機制,如果後面的表示式為真,則直接丟擲異常。在這裡的意思,大概就是:樣本和標籤數量要對上
if shuffle:
indices = np.arange(len(inputs)) #生成一個np.arange可迭代長度是len(訓練資料),也就是訓練資料第一維資料的數量(就是訓練資料的數量,訓練圖片的數量)。
np.random.shuffle(indices) #np.random.shuffle打亂arange中的順序,使其隨機循序化,如果是陣列,只打亂第一維。
for start_idx in range(0, len(inputs) - batch_size + 1, batch_size): # 這個range(初始值為0,終止值為[訓練圖片數-每批訓練圖片數+1],步長是[每批訓練圖片數]):例(0[起始值],80[訓練圖片數]-20[每批訓練圖片數],20[每批訓練圖片數]),也就是(0,60,20)當迴圈到60時,會加20到達80的訓練樣本.
if shuffle:
excerpt = indices[start_idx:start_idx + batch_size] # 如果shuffle為真,將indices列表,切片(一批)賦值給excerpt
else:
excerpt = slice(start_idx, start_idx + batch_size) # 如果shuffle為假,將slice()函式(切片函式),例項化,初始值為start_idx,結束為止為start_idx + batch_size(也就是根據上一批起始,算出本批結束的位置.),間距為預設.
yield inputs[excerpt], targets[excerpt]
#yield常見用法:該關鍵字用於函式中會把函式包裝為generator。然後可以對該generator進行迭代: for x in fun(param).
#按照我的理解,可以把yield的功效理解為暫停和播放。
#在一個函式中,程式執行到yield語句的時候,程式暫停,返回yield後面表示式的值,在下一次呼叫的時候,從yield語句暫停的地方繼續執行,如此迴圈,直到函式執行完。
#此處,就是返回每次迴圈中 從inputs和targets列表中,擷取的 經過上面slice()切片函式定義過的 資料.
#(最後的shuffle變數,決定了樣本是否隨機化)
def cnn_fc (input_tensor,train,regularizer):
with tf.variable_scope('layer1-conv1'): # 開啟一個聯絡上下文的名稱空間,空間名是layer1-conv1,在tf.get_variable可以順利呼叫
conv1_weights = tf.get_variable('weight',[5,5,3,6],initializer = tf.truncated_normal_initializer(stddev = 0.1))
#上面一行命令是生成卷積核:是一個tansor型別,具體含義是[卷積核的高度,卷積核的寬度,影象通道數,卷積核個數],要求型別與引數input相同,有一個地方需要注意,第三維in_channels,就是引數input的第四維
# tf.truncated_normal_initializer:從截斷的正態分佈中輸出隨機值。這是神經網路權重和過濾器的推薦初始值。
# mean:一個python標量或一個標量張量。要生成的隨機值的均值。
# stddev:一個python標量或一個標量張量。要生成的隨機值的標準偏差。
# seed:一個Python整數。用於建立隨機種子。檢視 tf.set_random_seed 行為。
# dtype:資料型別。只支援浮點型別。
conv1_biases = tf.get_variable("bias",[6],initializer = tf.constant_initializer(0.0))
conv1 = tf.nn.conv2d(input_tensor,conv1_weights,strides=[1,1,1,1],padding="SAME")
# 除去name引數用以指定該操作的name,與方法有關的一共五個引數:
#第一個引數input:指需要做卷積的輸入影象,它要求是一個Tensor,具有[batch, in_height, in_width, in_channels]這樣的shape,
#具體含義是[訓練時一個batch的圖片數量, 圖片高度, 圖片寬度, 影象通道數],注意這是一個4維的Tensor,要求型別為float32和float64其中之一
#第二個引數filter:相當於CNN中的卷積核,它要求是一個Tensor,具有[filter_height, filter_width, in_channels, out_channels]這樣的shape,
#具體含義是[卷積核的高度,卷積核的寬度,影象通道數,卷積核個數],要求型別與引數input相同,有一個地方需要注意,第三維in_channels,就是引數input的第四維
#第三個引數strides:卷積時在影象每一維的步長,這是一個一維的向量,長度4
#第四個引數padding:string型別的量,只能是"SAME","VALID"其中之一,這個值決定了不同的卷積方式(後面會介紹)
#第五個引數:use_cudnn_on_gpu:bool型別,是否使用cudnn加速,預設為true#
#結果返回一個Tensor,這個輸出,就是我們常說的feature map特徵圖,shape仍然是[batch, height, width, channels]這種形式。
relu1 = tf.nn.relu(tf.nn.bias_add(conv1,conv1_biases))
# 啟用函式,非最大值置零
# 這個函式的作用是計算啟用函式 relu,即 max(features, 0)。即將矩陣中每行的非最大值置0。
with tf.name_scope("layer2-pool1"):
pool1 = tf.nn.max_pool(relu1,ksize = [1,2,2,1], strides=[1,2,2,1],padding="VALID")
#tf.nn.max_pool(value, ksize, strides, padding, name=None)
#引數是四個,和卷積很類似:
#第一個引數value:需要池化的輸入,一般池化層接在卷積層後面,所以輸入通常是feature map,依然是[batch, height, width, channels]這樣的shape
#第二個引數ksize:池化視窗的大小,取一個四維向量,一般是[1, height, width, 1],因為我們不想在batch和channels上做池化,所以這兩個維度設為了1
#第三個引數strides:和卷積類似,視窗在每一個維度上滑動的步長,一般也是[1, stride,stride, 1]
#第四個引數padding:和卷積類似,可以取'VALID' 或者'SAME'
#返回一個Tensor,型別不變,shape仍然是[batch, height, width, channels]這種形式
with tf.variable_scope("layer3-conv2"):
conv2_weights = tf.get_variable("weight",[5,5,6,16],initializer=tf.truncated_normal_initializer(stddev=0.1))# [5,5,32,64] 5表示本次卷積核高寬,32表示經過上一層32個卷積核的卷積,我們有了32張特徵圖,64表明本次會有64個卷積核卷積
conv2_biases = tf.get_variable("bias", [16], initializer=tf.constant_initializer(0.0))
conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))
with tf.name_scope("layer4-pool2"):
pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')
nodes = 8*8*16
reshaped = tf.reshape(pool2,[-1,nodes]) # 其中pool2是連結上一層,我把pool4和pool3的卷積核池化層刪除了,卷的太多都要成渣渣了。
# tf.reshape(tensor(矩陣),shape(維度),name=None)
# 改變一個矩陣的維度,可以從多維變到一維,也可以從一維變到多維
# 其中,-1引數表示不確定,可由函式自己計算出來,原矩陣/一個維度=另一個維度
with tf.variable_scope('layer9-fc1'):
fc1_weights = tf.get_variable("weight", [nodes, 1024],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc1_weights))
# tf.add_to_collection:把變數放入一個集合,把很多變數變成一個列表
#在深度學習中,通常用這幾個函式存放不同層中的權值和偏置引數,
#也就是把所有可學習引數利用tf.contrib.layers.l2_regularizer(regular_num)(w)得到norm後,都放到’regular’的列表中作為正則項,
#然後使用tf.add_n函式將他們和原本的loss相加,得到含有正則的loss。
fc1_biases = tf.get_variable("bias", [1024], initializer=tf.constant_initializer(0.1))
fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases) # MCP模型
if train: fc1 = tf.nn.dropout(fc1, 0.5) #tf.nn.dropout是TensorFlow裡面為了防止或減輕過擬合而使用的函式,它一般用在全連線層。
# tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None,name=None)
# 上面方法中常用的是前兩個引數:
# 第一個引數x:指輸入的資料。
# 第二個引數keep_prob: 設定神經元被選中的概率,在初始化時keep_prob是一個佔位符, keep_prob = tf.placeholder(tf.float32) 。
# tensorflow在run時設定keep_prob具體的值,例如keep_prob: 0.5
# 第五個引數name:指定該操作的名字。
with tf.variable_scope('layer10-fc2'):
fc2_weights = tf.get_variable("weight", [1024, 512],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc2_weights))
fc2_biases = tf.get_variable("bias", [512], initializer=tf.constant_initializer(0.1))
fc2 = tf.nn.relu(tf.matmul(fc1, fc2_weights) + fc2_biases)
if train: fc2 = tf.nn.dropout(fc2, 0.5)
with tf.variable_scope('layer11-fc3'):
fc3_weights = tf.get_variable("weight", [512, 5],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc3_weights))
fc3_biases = tf.get_variable("bias", [5], initializer=tf.constant_initializer(0.1))
logit = tf.matmul(fc2, fc3_weights) + fc3_biases
# variable_summaries(fc3_weights)
# variable_summaries(fc3_biases)
return logit
# 設定超參
path = "E:\\study\\MINST-PLUS\\data" #樣本路徑
data_size = [32,32,3] #樣本大小和通道數
data_ratio = 0.9 # 百分之多少用於訓練,剩下的用於測試
is_train = False
epoch = 1 # 訓練次數
batch_size = 1 # 每次訓練多少
# 樣本和標籤的讀入與分類
data,label = read_img(path,data_size)
x_train,y_train,x_val,y_val = data_split_flow(data,label,data_ratio)
# 為資料與標籤設立兩個存放空間
x = tf.placeholder(tf.float32,shape=[None,data_size[0],data_size[1],data_size[2]],name = 'x')
y_ = tf.placeholder(tf.int32,shape=[None,],name = 'y_')
# 定義規則化方法,並計算網路啟用值
regularizer = tf.contrib.layers.l2_regularizer(0.0001) # 過擬合與正則化(regularizer),這個regularizer就是inference函式的最後一個引數。
#兩種思想都是希望限制權重的大小,使得模型不能擬合訓練資料中的隨機噪點。(兩種思想,就是兩個公式,因為是圖,就沒貼出來)
#兩種方式在TensorFlow中的提供的函式為:
#tf.contrib.layers.l1_regularizer(scale, scope=None) 其中scale為權值(這個權值會乘以w的值,MCP的內個w,江湖傳聞w和過擬合值有說不清的關係)
#tf.contrib.layers.l2_regularizer(scale, scope=None)
logits = cnn_fc(x,is_train,regularizer) #x是輸入的影象的tansor,logits是經過卷積、池化、全連線處理處理過的資料
#b = tf.constant(value=1,dtype=tf.float32) # constant(值、列表 , 陣列格式)根據值、列表,生成一個數組,格式為“陣列格式”
#logits_eval = tf.multiply(logits,b,name='logits_eval') # 額,不知道這是計算啥
# 計算誤差與準確率,並寫入日誌 (我沒有日誌,呵呵)
loss = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=logits) #計算logits 和 labels 之間的稀疏softmax 交叉熵 這個是計算誤差率
'''
train_op = tf.train.AdamOptimizer(learning_rate=0.01).minimize(loss) # tf.train.AdamOptimizer 優化器中的梯度優化函式,
# 作用是依據learning_rate步長,來最小化loss誤差率。
correct_prediction = tf.equal(tf.cast(tf.argmax(logits,1),tf.int32), y_)
#tf.argmax(vector, 1):返回的是vector中的最大值的索引號,如果vector是一個向量,那就返回一個值,如果是一個矩陣,那就返回一個向量,
#這個向量的每一個維度都是相對應矩陣行的最大值元素的索引號。
# tf.cast(x, dtype, name=None)
# 此函式是型別轉換函式
# 引數
# x:輸入
# dtype:轉換目標型別
# name:名稱
# 返回:Tensor
# tf.equal(A, B)是對比這兩個矩陣或者向量的相等的元素,如果是相等的那就返回True,反正返回False,
# 返回的值的矩陣維度和A是一樣的,返回的也是一個矩陣、向量、列表,裡面都是true和false。
#這一行的意思,大概是,通過以上三個函式,對比處理後的logits值和labels值,然後得出一個判斷表單
acc= tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) #這個......大概是在計算準確率
# 求平均值tf.reduce_mean(input_tensor, reduction_indices=None, keep_dims=False, name=None)
# 引數1--input_tensor:待求值的tensor。
# 引數2--reduction_indices:在哪一維上求解。
# 引數(3)(4)可忽略
# tf.summary.scalar('accuracy', acc)
'''
# 建立儲存點,並進入計算圖流程 還有限制gpu,我的電腦沒有這句話就各種死
#saver=tf.train.Saver()
sess=tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)) #限制gpu記憶體
sess.run(tf.global_variables_initializer()) # 全世界get_variable嗨起來
for epoch in range(epoch): # 訓練多少遍,epoch是使用者輸入的,比如是5,也就是把樣本遍歷5遍
start_time = time.time() # 開始計時
### 單次訓練部分 此處for迴圈結束之日,就是訓練樣本遍歷了一遍之時......
# train_loss, train_acc, n_batch = 0, 0, 0 # 先定義下訓練誤差,訓練識別率,訓練批次
for x_train_a, y_train_a in minibatches(x_train, y_train,batch_size, shuffle=False):
#遍歷minibatches函式,因為這個函式中有yield關鍵字,每次for,會獲取到不同的批次,直到訓練樣本集合身體被掏空。注意,這裡shuffle為True,樣本是隨機的。
log,los = sess.run([logits,loss], feed_dict={x: x_train_a, y_: y_train_a}) #向sess.run中喂資料,
# 其中merged是train_summary計算圖;train_op是梯度優化方法,err接收的loss是誤差率;ac接收的acc是準確率。後面的x和y_就是每批的資料和標籤。
# train_loss += err; train_acc += ac; n_batch += 1 #統計誤差率、準確率、批次
print(log,los) #輸出每個樣本的logits值和loss值
print()
print()
sess.close()
然後這個是160個樣本的logits值和loss值的對應:
[[ -339.9708 -69.041725 -1067.7036 -245.84515 138.52219 ]] nan
[[-158.06973 -246.89626 -935.557 -248.51776 103.3337 ]] 0.0
[[ -155.45343 -174.68727 -1113.9407 -227.96527 162.82603]] nan
[[ -217.28845 -243.36374 -1009.486 -198.31784 119.12733]] 336.41577
[[-236.27396 -133.29968 -883.61383 -307.63428 185.67291]] nan
[[-149.70811 -132.09709 -886.4549 -129.16461 140.89886]] nan
[[-115.13487 -173.27644 -987.3448 -254.20341 218.48964]] nan
[[-326.896 -308.39993 -914.8617 -134.75952 150.18616]] 1065.0479
[[-473.68124 -214.30078 -982.75653 -324.08688 253.72874]] 0.0
[[-180.8921 -145.02092 -742.64105 -172.11078 -16.130255]] 164.76186
[[ -225.14946 -161.39912 -1025.3687 -318.29987 178.14491]] nan
[[-205.64194 -327.26382 -957.71124 -138.00604 41.8925 ]] 369.1563
[[ -200.25946 -217.56631 -1017.56995 -153.5807 88.18885]] nan
[[ -114.34741 -286.7671 -1103.8845 -76.43528 187.08517]] nan
[[-191.44543 -254.11757 -897.7715 -71.173195 40.45874 ]] 0.0
[[ -210.87175 -183.36429 -1189.2098 -205.37778 193.08887]] nan
[[-296.5331 -314.5609 -943.02246 -319.9631 147.95262]] 462.51355
[[ -225.04294 -188.95775 -1016.372 -78.7354 121.46053]] 346.50348
[[-397.52747 -244.8122 -997.7895 -298.903 158.88753]] 1156.677
[[-197.89807 -306.24576 -971.24677 -268.07953 151.33243]] 419.41196
[[-124.87983 -265.53268 -998.7135 -178.9643 40.895084]] nan
[[ -189.38452 -101.057785 -1135.6907 -245.18817 169.72134 ]] nan
[[ -208.67839 -97.87629 -1034.4067 -319.19882 201.7934 ]] nan
[[-303.44107 -331.08414 -957.3063 -255.90576 -71.63695]] 231.80412
[[ -169.57759 -108.55869 -1209.5519 -172.89476 208.19168]] nan
[[-147.5819 -171.07469 -910.13245 -82.26827 86.733116]] nan
[[ -114.29173 -207.91628 -1032.6654 -252.88803 140.07599]] 392.96402
[[ -262.4568 -229.78604 -1120.4459 -311.7682 114.07547]] 1234.5214
[[ -98.569 -274.89136 -851.79877 -175.66774 44.803627]] 319.69498
[[-203.76027 -314.2957 -964.86017 -179.71866 93.350426]] 297.1107
[[ -343.4648 -87.47349 -1003.65076 -229.2045 93.31492]] nan
[[-306.13544 -275.4731 -985.6897 -212.52412 229.50276]] 1215.1925
[[-246.24588 -172.22351 -979.2212 -365.32474 167.35965]] nan
[[ -171.15607 -185.04291 -1040.1666 -321.16483 158.0437 ]] nan
[[ -242.58545 -276.06787 -1102.614 -254.65155 33.01659]] 1135.6306
[[-319.27502 -255.60544 -993.10474 -222.13464 102.73994]] nan
[[ -232.80014 -169.73009 -1049.5348 -278.7196 106.64243]] 0.0
[[ -283.4286 -2