1. 程式人生 > >卷積神經網路中loss值為nan的問題(已解決)

卷積神經網路中loss值為nan的問題(已解決)

卷積神經網路中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