【學習筆記】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 進行操作會報錯,具體報錯什麼記不清了。因此採用了這種方法。