深度學習 神經網絡 神經元 單層神經網絡的實現

使用 tensorflow1.14, 數據集 cifar10

代碼

初始化

import tensorflow as tf
import pickle as pk
import numpy as np
import os

CIFAR_DIR = './cifar-10-batches-py'

數據集一覽

data_batch_1 ~ 5 爲訓練數據集, test_batch 爲測試集

os.listdir(CIFAR_DIR)
運行結果
['batches.meta',
 'data_batch_1',
 'data_batch_2',
 'data_batch_3',
 'data_batch_4',
 'data_batch_5',
 'readme.html',
 'test_batch']

看看圖片的shape, 標籤等東西

with open(os.path.join(CIFAR_DIR, "data_batch_1"), "rb") as f:
    data = pk.load(f, encoding='bytes')  # load 默認使用ascii解碼, 這裏要用二進制來
    print(type(data))  # <class 'dict'>
    print(data.keys())  # dict_keys([b'batch_label', b'labels', b'data', b'filenames'])
    
    print(type(data[b'batch_label']))  # <class 'bytes'>
    print(type(data[b'labels']))  # <class 'list'>
    print(type(data[b'data']))  # <class 'numpy.ndarray'>
    print(type(data[b'filenames']))  # <class 'list'>
    print("----------------------")
    print(data[b'batch_label'])  # b'training batch 1 of 5'
    print(data[b'labels'][0:2])  # [6, 9]
    print(data[b'data'].shape)  # 10000張圖片, 32*32*3圖片
    print(data[b'data'][0:2])  
    print(data[b'filenames'][0:2])  # [b'leptodactylus_pentadactylus_s_000004.png', b'camion_s_000148.png']
運行結果
<class 'dict'>
dict_keys([b'batch_label', b'labels', b'data', b'filenames'])
<class 'bytes'>
<class 'list'>
<class 'numpy.ndarray'>
<class 'list'>
----------------------
b'training batch 1 of 5'
[6, 9]
(10000, 3072)
[[ 59  43  50 ... 140  84  72]
 [154 126 105 ... 139 142 144]]
[b'leptodactylus_pentadactylus_s_000004.png', b'camion_s_000148.png']

打開一張圖片看看, 像素有點低…

img_arr = data[b'data'][6666]
img_arr = img_arr.reshape((3, 32, 32))  # 一張圖片3072個像素, 前32*32是R層, 然後依次是G, B層, 所以得先分三層, 再分成32*32 
img_arr = img_arr.transpose((1, 2, 0))  # 調換位置成 32 32 3 的格式, 轉成這樣後一個32*3就能代表一行的像素, 32行就是一張圖片 

import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
%matplotlib inline
plt.imshow(img_arr)

在這裏插入圖片描述

數據預處理

  • 定義一個數據預處理的類
  • 設置訓練數據集

也可以使用 tensorflow.Dataset 來實現

def load_data(filename):
    """讀文件數據"""
    with open(filename, 'rb') as f:
        data = pk.load(f, encoding='bytes')
        return data[b'data'], data[b'labels']
    
class CifarData:
    """對Cifar10的數據進行處理"""
    def __init__(self, filenames, need_shuffle):
        """
        filename: 訓練或者測試數據集的名稱
        need_shuffle: 數據過完一遍之後(一個batch之後)是否需要打亂. 測試數據集不需要打亂
        """
        all_data = []  # 存儲一個個圖片向量
        all_labels = []  # 存儲圖片標籤
        for filename in filenames:
            data, labels = load_data(filename)
            for item, label in zip(data, labels):
                if label in [0, 1]: # 做二分類任務, 只取0, 1兩個標籤的圖
                    all_data.append(item) # 把label是0或1的圖片加入
                    all_labels.append(label)
                    
        self._data = np.vstack(all_data) # 將all_data變成 圖片數量*3072 的矩陣
#         self._data = self._data / 255
        self._data = self._data / 127.5 - 1 # 歸一化, 範圍調整在-1, 1之間
        print(self._data.shape)  # test
        
        self._labels = np.hstack(all_labels) # 變成一長行
        print(self._labels.shape)  # test
        
        self._num_examples = self._data.shape[0]  # 圖片數量
        self._need_shuffle = need_shuffle # 訓練集需要打亂, 測試不需要, 打亂增強泛化能力
        self._indicator = 0 # 存儲當前的位置: 一個一個batch送到計算圖去運算, _indicator用來指明當前遍歷到的位置
        if self._need_shuffle:
            self._shuffle_data()
            
    def _shuffle_data(self):
	    """打亂"""
        p = np.random.permutation(self._num_examples)  # 得到一個 0~_num_examples 的混亂序列
        self._data = self._data[p]  # 把數據按照一個混亂的序列去排, 達到打亂的效果
        self._labels = self._labels[p]  # 做跟 _data 一樣的打亂
        
    def next_batch(self, batch_size): # 批次(樣本)數目
        """return batch_size examples as a batch"""
        if batch_size > self._num_examples:  # 如果batch_size比整個數據集都大
            raise Exception("batch size is larger than all examples")
            
        end_indicator = self._indicator + batch_size  # batch的結束位置 
        if end_indicator > self._num_examples:
            if self._need_shuffle:  # 允許打亂, 則複用之前的數據
                self._shuffle_data()
                self._indicator = 0
                end_indicator = batch_size # 0 + batch_size
            else:  # 沒有數據了
                raise Exception("have no more examples. if this is the first batch, then the batch size is lager than all examples. ")
            
        batch_data = self._data[self._indicator: end_indicator]  # batch大小的數據
        batch_labels = self._labels[self._indicator: end_indicator]  # 對應的label
#         self._indicator += batch_size
        self._indicator = end_indicator  # 維護indicator的值
        return batch_data, batch_labels 

train_filenames = [os.path.join(CIFAR_DIR, "data_batch_%d" % i) for i in range(1, 6)]
test_filenames = [os.path.join(CIFAR_DIR, "test_batch")]

train_data = CifarData(train_filenames, need_shuffle=True)
test_data = CifarData(test_filenames, need_shuffle=False)  # 測試機不需要打亂

############## test ###############
batch_data, batch_labels = train_data.next_batch(10)
print(batch_data.shape)
print(batch_data)
print(batch_labels)
運行結果
(10000, 3072)  # 訓練集數據
(10000,)  # 訓練集標籤
(2000, 3072)  # 測試集數據
(2000,)  # 測試集標籤
(10, 3072)  # 一個batch10張圖
[[ 1.          1.          1.         ...  1.          1.
   1.        ]
 [-0.35686275 -0.43529412 -0.42745098 ... -0.03529412  0.12156863
   0.12156863]
 [ 0.09019608  0.01960784  0.12156863 ... -0.82745098 -0.85882353
  -0.54509804]
 ...
 [ 0.05098039  0.15294118  0.10588235 ...  0.33333333  0.31764706
   0.30196078]
 [ 0.08235294 -0.12941176 -0.12156863 ...  0.27843137  0.24705882
   0.23921569]
 [ 0.5372549   0.42745098  0.56862745 ... -0.25490196 -0.25490196
  -0.2627451 ]]
[1 0 0 0 0 0 0 0 1 1]

構建模型圖

  1. 設置數據 xx, 數據標籤 yy
  2. 初始化權重 WW (最後要訓練的), 以及偏置 bb
  3. 設置 xW+bx \cdot W + b, 並轉爲概率值
  4. 定義損失函數, 準確率
  5. 定義優化器, 來最小化損失函數
x = tf.placeholder(tf.float32, [None, 3072])  # 圖片
y = tf.placeholder(tf.int64, [None])  # 圖片label
# None 表示還不確定幾張圖片, 或者說圖片的數量可變, 因爲 batch size可以被調整
# placeholder表示佔位, 先構建計算圖的結構


# shape [3072, 1]
w = tf.get_variable('w', [x.get_shape()[-1], 1],  # 大小爲 x 的shape
                   initializer = tf.random_normal_initializer(0, 1))  # 使用正太分佈初始化
# shape [1, ]
b = tf.get_variable('b', [1],  # 偏置, 維度與 w 的第二維一樣
                   initializer = tf.constant_initializer(0.0))  # 使用常量初始化


# shape [None, 3072] * [3072, 1] = [None, 1]
y_ = tf.matmul(x, w) + b  # matrix multiple 矩陣乘
# shape [None, 1]
predict_y_1 = tf.nn.sigmoid(y_)  # 將 y_變爲概率值
# shape [None] -> [None, 1]
y_reshaped = tf.reshape(y, (-1, 1)) # 轉成一樣的才能做差別分析
y_reshaped_float = tf.cast(y_reshaped, tf.float32) # tf對類型很敏感


# 均方誤差
loss = tf.reduce_mean(tf.square(y_reshaped_float - predict_y_1))


# 準確率
predict = predict_y_1 > 0.5 # bool 類似[true, false, true, true...]
correct_prediction = tf.equal(tf.cast(predict, tf.int64), y_reshaped)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float64))

# 定義一個梯度下降的方法
with tf.name_scope('train_op'):
    train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)  # 學習率 0.001

神經元

init = tf.global_variables_initializer()  # 用來執行初始化
batch_size = 20  # 一次喂20張圖片
train_steps = 100000  # 訓練次數
test_steps = 100  # 訓練集一共2000張圖片, 一次喂20張, 所以一共喂100次(因爲數據不打亂, 所以要嚴格等於)
# 一個神經元
with tf.Session() as sess:
    sess.run(init)
    for i in range(train_steps):
        batch_data, batch_labels = train_data.next_batch(batch_size)
        loss_val, acc_val, _ = sess.run(
            [loss, accuracy, train_op],  # 計算loss, accuracy, 然後訓練, 如果不加train_op則只計算不訓練
            feed_dict={
                x: batch_data,
                y: batch_labels,
            })
        
        if (i+1) % 500 == 0:
            print("[Train] Step: %d, loss: %4.5f, acc: %4.5f" \
                 % (i+1, loss_val, acc_val))
            
        # 通過測試集看準確率
        if (i+1) % 5000 == 0:
            test_data = CifarData(test_filenames, need_shuffle=False)  # 因爲沒有打亂, 數據集會被用完, 所以每次得新建一個CifarData
            all_test_acc_val = []  # 存放準確率
            for j in range(test_steps):
                test_batch_data, test_batch_labels = test_data.next_batch(batch_size)
                test_acc_val = sess.run(
                    [accuracy], feed_dict = {
                        x: test_batch_data,
                        y: test_batch_labels
                    }
                )
                all_test_acc_val.append(test_acc_val)
            
            test_acc = np.mean(all_test_acc_val)
            print("[Test] Step: %d, acc: %4.5f" % (i+1, acc_val))
訓練結果
[Train] Step: 500, loss: 0.24986, acc: 0.75000
[Train] Step: 1000, loss: 0.24355, acc: 0.75000
[Train] Step: 1500, loss: 0.15000, acc: 0.85000
[Train] Step: 2000, loss: 0.33586, acc: 0.65000
[Train] Step: 2500, loss: 0.10048, acc: 0.90000
[Train] Step: 3000, loss: 0.39996, acc: 0.60000
[Train] Step: 3500, loss: 0.14202, acc: 0.85000
[Train] Step: 4000, loss: 0.14709, acc: 0.85000
[Train] Step: 4500, loss: 0.20128, acc: 0.80000
[Train] Step: 5000, loss: 0.10128, acc: 0.90000
(2000, 3072)
(2000,)
.......
(2000,)
[Test] Step: 95000, acc: 0.70000
[Train] Step: 95500, loss: 0.25005, acc: 0.75000
[Train] Step: 96000, loss: 0.05000, acc: 0.95000
[Train] Step: 96500, loss: 0.20000, acc: 0.80000
[Train] Step: 97000, loss: 0.05003, acc: 0.95000
[Train] Step: 97500, loss: 0.25051, acc: 0.75000
[Train] Step: 98000, loss: 0.10230, acc: 0.90000
[Train] Step: 98500, loss: 0.19962, acc: 0.80000
[Train] Step: 99000, loss: 0.15157, acc: 0.85000
[Train] Step: 99500, loss: 0.20032, acc: 0.80000
[Train] Step: 100000, loss: 0.10221, acc: 0.90000
(2000, 3072)
(2000,)
[Test] Step: 100000, acc: 0.90000

單層神經網絡

需要修改原來的 CifarData的初始化函數, 不再只取兩種標籤的數據

class CifarData:
    def __init__(self, filenames, need_shuffle):
        all_data = []
        all_labels = []
        for filename in filenames:  # 十個種類的數據
            data, labels = load_data(filename)
            all_data.append(data)
            all_labels.append(labels)
        self._data = np.vstack(all_data) # 將all_data變成 圖片數量*3072 的矩陣
        self._data = self._data / 127.5 - 1 # 歸一化
        self._labels = np.hstack(all_labels) 
        self._num_examples = self._data.shape[0]
        self._need_shuffle = need_shuffle
        self._indicator = 0
        if self._need_shuffle:
            self._shuffle_data()

模型圖需要修改 loss函數, 準確率

x = tf.placeholder(tf.float32, [None, 3072]) # None 表示還不確定幾張圖片
y = tf.placeholder(tf.int64, [None])

# shape: [3072, 10]
w = tf.get_variable('w', [x.get_shape()[-1], 10], 
                   initializer = tf.random_normal_initializer(0, 1))

# shape: [10, ]
b = tf.get_variable('b', [10],
                   initializer = tf.random_normal_initializer(0, 1))

# shape: [None, 3072] * [3072, 10] = [None, 10]
y_ = tf.matmul(x, w) + b

# 平方差損失
"""
# something like [[0.01, 0.9,..., 0.03], [...],...], 每一項有10個值, 分別代表可能label幾的概率
predict_y = tf.nn.softmax(y_) # e^x / sum(e^x) 
y_one_hot = tf.one_hot(y, depth=10, dtype=tf.float32)  # 轉爲 onehot 編碼
loss = tf.reduce_mean(tf.square(y_one_hot - predict_y))
"""
# 交叉熵損失
loss = tf.losses.sparse_softmax_cross_entropy(labels=y, logits=y_)
# 包含的步驟: 1. y_->sofmax; 2. y->one_hot; 3. loss = ylogy_

# shape: [None,]
predict = tf.argmax(y_, 1)  # 預測值. 返回y_的第二維中最大值的索引, 即爲預測結果
correct_prediction = tf.equal(predict, y)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float64))

with tf.name_scope('train_op'):
    train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)

訓練部分代碼不需要改變
如果報錯

注意點

  • 圖片一般要歸一化處理, 因爲圖片的數值普遍較大, 使用sigmoid容易偏向一方, 最後的導致梯度消失

other

  • 數據集下載
    鏈接:https://pan.baidu.com/s/1sI90OHBjWvuJZDmmdj9pFA
    提取碼:cnwp
  • InternalError: 2 root error(s) found. 均爲 Internal: Blas GEMM launch failed
    solution 加入
    os.environ["CUDA_VISIBLE_DEVICES"] = "0"
    config = tf.ConfigProto(allow_soft_placement = True)
    gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 0.5)
    config.gpu_options.allow_growth = True
     
    sess0 = tf.InteractiveSession(config = config)
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章