1. 程式人生 > >記一次使用Tensorflow搭建神經網路模型經歷

記一次使用Tensorflow搭建神經網路模型經歷

隱去背景, 作者最近第一次用Tensorflow實現訓練了幾個模型, 其中遇到了一些錯誤, 把它記錄下來

前言

以下提到的所有程式碼, 都可以在github上面找到. 倉庫地址 https://github.com/spxcds/neural_network_code/
這個倉庫裡提到的幾段程式碼, 分別實現在從最簡單的lr, 到全連線神經網路, 再到卷積神經網路. 從最簡單的自己實現交叉熵損失函式, 計算L2正則化, 到後來直接呼叫庫函式, 由簡到難, 由淺入深, 截止目前為止, 只實現了MLR, MLP, LeNet-5, AlexNet, VGG-16等幾個演算法

網路結構

LeNet-5

AlexNet

程式碼實現

幾個重要的函式

卷積操作

def conv(self, input_tensor, name, kh, kw, dh, dw, n_output, padding='SAME'):
    n_input = input_tensor.get_shape()[-1].value

    kernel = tf.get_variable(
        name=name + 'kernel',
        shape=[kh, kw, n_input, n_output],
        dtype=tf.float32,
        initializer=tf.truncated_normal_initializer(stddev=0.05))
    bias = tf.get_variable(
        name=name + 'bias', shape=[n_output], dtype=tf.float32, initializer=tf.constant_initializer(0.0))

    c = tf.nn.conv2d(input_tensor, kernel, (1, dh, dw, 1), padding=padding) # SAME, VALID
    return tf.nn.relu(tf.nn.bias_add(c, bias), name=name)

全連線操作

def fc(self, input_tensor, name, n_output):
    n_input = input_tensor.get_shape()[-1].value
    weights = tf.get_variable(
        name=name + 'weights',
        shape=[n_input, n_output],
        dtype=tf.float32,
        initializer=tf.truncated_normal_initializer(stddev=0.05))
    tf.add_to_collection('losses', tf.nn.l2_loss(weights)) # l2_lambda * tf.add_n(tf.get_collection('losses'))
    bias = tf.get_variable(
        name=name + 'bias', shape=[n_output], dtype=tf.float32, initializer=tf.constant_initializer(0.0))

    return tf.nn.bias_add(tf.matmul(input_tensor, weights), bias)

交叉熵

cost_cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(tf.clip_by_value(p, 1e-10, 1.0)), axis=1))

畫圖

def plot(self, save_path):
    df = pd.DataFrame(self.train_history, columns=['iterations', 'train_acc', 'val_acc', 'train_loss', 'val_loss'])

    # loss曲線
    fig = plt.figure(figsize=(20, 10))
    ax = fig.add_subplot(121)
    ax.grid(True)
    ax.plot(df.iterations, df.train_loss, 'k', label='訓練集損失', linewidth=1.2, alpha=0.4)
    ax.plot(df.iterations, df.val_loss, 'k--', label='驗證集損失', linewidth=2)
    ax.legend(fontsize=16)
    ax.set_xlabel('Iterations', fontsize=16)
    ax.set_ylabel('Loss', fontsize=16)
    ax.set_xlim(np.min(df.iterations), np.max(df.iterations) + 0.1, auto=True)
    ax.tick_params(axis='both', which='major')
    ax.set_title('損失曲線', fontsize=22)

    # 混淆矩陣
    fig_matrix_confusion = plt.figure(figsize=(10, 10))
    ax = fig_matrix_confusion.add_subplot(111)
    confusion_matrix = self.get_confusion_matrix(mnist.test.images, mnist.test.labels)
    sns.heatmap(
        confusion_matrix,
        fmt='',
        cmap=plt.cm.Greys,
        square=True,
        cbar=False,
        ax=ax,
        annot=True,
        xticklabels=np.arange(10),
        yticklabels=np.arange(10),
        annot_kws={'fontsize': 20})
    ax.set_xlabel('Predicted', fontsize=16)
    ax.set_ylabel('True', fontsize=16)
    ax.tick_params(labelsize=14)
    ax.set_title('混淆矩陣', fontsize=22)
    plt.savefig(save_path + '_confusion_matrix')
    plt.close()

碰到的問題

  1. 網路loss幾乎不收斂
    • 學習率設定的不對, 稍微調大一點學習率就可以了
    • batch_size設定的太大
    • 優化演算法選一個更高階的, 原先我使用的是tf.train.GradientDescentOptimizer優化演算法, 跑了幾千個batch才有效果, 換成tf.train.AdamOptimizer, 幾十個batch就開始收斂了
  2. 訓練一段時間後, 網路loss變為NaN
    • 梯度爆炸, 使學習過程偏離了正常的學習軌跡, 這個時候調低學習率就可以了
    • 計算交叉熵的時候, 出現了log(0)*0的;情況, 使用tf.clip_by_value(t=value,clip_value_min=1e-8,clip_value_min=1.0)避免這種情況
  3. 訓練集和驗證集accuracy維持在0.1左右不變, 可能正則化引數l2_lambda設定大了, 設成1e-4左右試一下
  4. 全連線層的最後一層輸出層就不要加relu了, 直接加一個softmax即可

未經允許禁止轉載 http://spxcds.com/2019/01/01/first_deep_learning/