1. 程式人生 > >【學習筆記】softmax迴歸與mnist程式設計

【學習筆記】softmax迴歸與mnist程式設計

我們之前談到了2元分類,但是有時候我們需要多元分類,這時候sigmoid函式就不再適用了。

假如我們需要三個分類,而輸出層在啟用函式之前得到的值為3.,4.,5. ,如果我們用sigmoid:

sess.run(tf.nn.sigmoid([3.,4.,5.]))
array([0.95257413, 0.98201376, 0.9933072 ], dtype=float32)

我們可以看到,輸出結果並不能很好的分類。如果改用softmax:

sess.run(tf.nn.softmax([3.,4.,5.]))
array([0.09003057, 0.24472848, 0.66524094], dtype=float32)

狀況則要好得多。

下面是原文對softmax的介紹:

Softmax 選項

請檢視以下 Softmax 變體:

  • 完整 Softmax 是我們一直以來討論的 Softmax;也就是說,Softmax 針對每個可能的類別計算概率。

  • 候選取樣指 Softmax 針對所有正類別標籤計算概率,但僅針對負類別標籤的隨機樣本計算概率。例如,如果我們想要確定某個輸入圖片是小獵犬還是尋血獵犬圖片,則不必針對每個非狗狗樣本提供概率。

類別數量較少時,完整 Softmax 代價很小,但隨著類別數量的增加,它的代價會變得極其高昂。候選取樣可以提高處理具有大量類別的問題的效率。

一個標籤與多個標籤

Softmax 假設每個樣本只是一個類別的成員。但是,一些樣本可以同時是多個類別的成員。對於此類示例:

  • 您不能使用 Softmax。
  • 您必須依賴多個邏輯迴歸。

例如,假設您的樣本是隻包含一項內容(一塊水果)的圖片。Softmax 可以確定該內容是梨、橙子、蘋果等的概率。如果您的樣本是包含各種各樣內容(幾碗不同種類的水果)的圖片,您必須改用多個邏輯迴歸。

下面是mnist,學神經網路肯定會在不同程度上接觸mnist資料集,這裡我們用dnn的框架來識別mnist圖片(cnn效果更佳)。

import numpy as np
from MNIST_data import input_data
from tensorflow.data import Dataset
import tensorflow as tf
import pandas as pd
from tensorflow.contrib import layers
import matplotlib.pyplot as plt

mnist = input_data.read_data_sets('./MNIST_data', one_hot=True)


def random_data(xs, ys):
    df_xs = pd.DataFrame(xs)
    df_ys = pd.DataFrame(ys)
    df_concat = pd.concat([df_xs, df_ys], axis=1)
    df_concat = df_concat.reindex(np.random.permutation(df_concat.index))
    df_concat = df_concat.sort_index()
    df_features = df_concat.iloc[::, 0:784]
    df_targets = df_concat.iloc[::, 784::]
    return np.matrix(df_features), np.matrix(df_targets)


def my_input_fn(features, labels, batch_size=1, num_epochs=1, shuffle=False):
    ds = Dataset.from_tensor_slices((features, labels))
    ds = ds.batch(batch_size).repeat(num_epochs)
    if shuffle:
        ds.shuffle(10000)
    features, labels = ds.make_one_shot_iterator().get_next()
    return features, labels


def add_layer(inputs, inputs_size, outputs_size, activation_function=None):
    weights = tf.Variable(tf.random_normal([inputs_size, outputs_size], stddev=.1))
    tf.add_to_collection('loss', layers.l1_regularizer(0.001)(weights))
    biases = tf.Variable(tf.zeros([outputs_size]) + 0.1)
    wx_b = tf.matmul(inputs, weights) + biases
    if activation_function is None:
        outputs = wx_b
    else:
        outputs = activation_function(wx_b)
    return weights, biases, outputs


def _loss(pred, ys):
    loss = -tf.reduce_sum(ys*tf.log(pred))
    loss_total = loss + tf.add_n(tf.get_collection('loss'))
    return loss_total


def train_step(learning_rate, loss):
    train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss, global_step=global_step)
    return train_step


def accuracy(vx_pred, vy):
    correct_prediction = tf.equal(tf.argmax(vx_pred, 1), tf.argmax(vy, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    return accuracy


xs = mnist.train.images
ys = mnist.train.labels
ys = ys.astype('float32')
xs, ys = random_data(xs, ys)
vx = mnist.validation.images
vy = mnist.validation.labels
vy = vy.astype('float32')
global_step = tf.Variable(0, trainable=False)
start_learning_rate = .001
lr = tf.train.exponential_decay(start_learning_rate, global_step, 100, 0.95, staircase=True)

x_input, y_input =my_input_fn(xs, ys, batch_size=50, num_epochs=2)
vx_input, vy_input = my_input_fn(vx, vy, batch_size=5000, num_epochs=40)
w1, b1, l1 = add_layer(x_input, 784, 200, activation_function=tf.nn.tanh)
w2, b2, l2 = add_layer(l1, 200, 100, activation_function=tf.nn.tanh)
w3, b3, pred = add_layer(l2, 100, 10, activation_function=tf.nn.softmax)

loss = _loss(pred, y_input)
train = train_step(lr, loss)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

vl1 = tf.nn.tanh(tf.matmul(vx_input, w1) + b1)
vl2 = tf.nn.tanh(tf.matmul(vl1, w2) + b2)
vpred = tf.matmul(vl2, w3) + b3
v_accuracy = accuracy(vpred, vy)

for i in range(2000):
    sess.run(train)
    if i % 50 == 0:
        print(sess.run(v_accuracy))


img = sess.run(w1).copy()
plt.figure(1)

for i in range(200):
    plt.subplot(10, 20, i+1)
    plt.imshow(img[:, i].reshape(28, 28), cmap='binary')


plt.show()

匯入後我們發現labels的dtype是float64我們先改成float32。

下一步我們對訓練集打亂順序,方法與之前一樣,先把矩陣變成DataFrame,然後打亂index再變回矩陣。

這裡我改用了動態的learning_rate(直接設定一個引數也可以)。

tf.train.exponential_decay的幾個引數,首先是初始學習速率,其次是步數,下一個是每多少步更新多少速率,下一個是更新後的速率。

這裡其實就是每隔100論,用lr*0.95, staircase就是是否每隔100論再更新,預設為false,如果是false的話,每一步都會進行更新,但是整體速率是不變的。

loss與我們之前寫的不太一樣,這裡是交叉熵函式。之所以沒有了-ys*tf.log(1-pred)是因為  softmax中每一個引數調整均會影響其他引數,前面已經介紹過了,不信你可以試試 tf.nn.softmax([3,4,5]) 和 tf.nn.softmax([3,4,6])輸出的結果有何不同。

這裡我們輸入的784個features有很多0,我希望可以讓部分權重變為0,原因以前提到過,節省RAM並且降低噪點。如果忘記了可以往前翻一翻或者直接看官方教程的(正則化:稀疏性)。

argmax則是找到當前最大值所在的位置,這裡舉個例子:

>>>x = np.eye(5)
>>>print(sess.run(tf.argmax(x,1)))

array([0, 1, 2, 3, 4])

tf.equal則是判斷元素是否相等返回布林值, tf.cast則是轉化為其他型別,我們這裡轉化為tf.float32.

依舊舉個例子:

>>>print(sess.run(tf.equal(3,5)))

False


>>>print(sess.run(tf.cast(tf.equal([3,4],[5,4]), tf.float32)))

array([0., 1.], dtype=float32)

至於圖片顯示,對我這種古董電腦真的很吃力。因此我在訓練完成後顯示到了一個figure中,不過什麼也看不清。

figure過多的話,非常佔用記憶體,我們這裡可以建立10個figure,每個顯示20張圖片,或者乾脆將第一層的weights數減少,這樣更方便觀看變化。

最後的準確率應該在96%-97%左右。如果你願意調參的話,最後應該會到98%以上,但是表現依舊不如cnn(不過dnn執行起來速度要快的多的多)。

官方cnn的教程應該還會用到mnist集,後續問題我們之後再談。

最後說一下我資料集的位置。我的是在當前py檔案目錄下建立了一個資料夾叫 MNIST_data,並且把網上下載好的4個.gz的壓縮檔案放入其中,並且把tensorflow.examples.tutorials.mnist.input_data.py 複製到了該目錄下,因此直接import

在遠古版本的tensorflow中, 直接from tensorflow.examples.tutorials.mnist import input_data 進行操作會報錯,具體報錯什麼記不清了。因此採用了這種方法。