1. 程式人生 > >mnist手寫字識別及tensorflow與tflearn對比

mnist手寫字識別及tensorflow與tflearn對比

一、mnist機器學習入門

MNIST是一個入門級的計算機視覺資料集,它包含各種手寫數字圖片:

它也包含每一張圖片對應的標籤,告訴我們這個是數字幾。比如,上面這四張圖片的標籤分別是5,0,4,1。

在此教程中,我們將訓練一個機器學習模型用於預測圖片裡面的數字。我們的目的不是要設計一個世界一流的複雜模型 -- 儘管我們會在之後給你原始碼去實現一流的預測模型。所以,我們這裡會從一個很簡單的數學模型開始,它叫做Softmax Regression。

對應這個教程的實現程式碼很短,而且真正有意思的內容只包含在三行程式碼裡面。但是,去理解包含在這些程式碼裡面的設計思想是非常重要的:TensorFlow工作流程和機器學習的基本概念。因此,這個教程會很詳細地介紹這些程式碼的實現原理。

MNIST資料集

MNIST資料集的官網是Yann LeCun's website。在這裡,我們提供了一份python原始碼用於自動下載和安裝這個資料集。你可以下載這份程式碼,然後用下面的程式碼匯入到你的專案裡面,也可以直接複製貼上到你的程式碼檔案裡面。

import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

下載下來的資料集被分成兩部分:60000行的訓練資料集(mnist.train)和10000行的測試資料集(mnist.test

)。這樣的切分很重要,在機器學習模型設計時必須有一個單獨的測試資料集不用於訓練而是用來評估這個模型的效能,從而更加容易把設計的模型推廣到其他資料集上(泛化)。

正如前面提到的一樣,每一個MNIST資料單元有兩部分組成:一張包含手寫數字的圖片和一個對應的標籤。我們把這些圖片設為“xs”,把這些標籤設為“ys”。訓練資料集和測試資料集都包含xs和ys,比如訓練資料集的圖片是 mnist.train.images ,訓練資料集的標籤是 mnist.train.labels

每一張圖片包含28X28個畫素點。我們可以用一個數字陣列來表示這張圖片:

我們把這個陣列展開成一個向量,長度是 28x28 = 784。如何展開這個陣列(數字間的順序)不重要,只要保持各個圖片採用相同的方式展開。從這個角度來看,MNIST資料集的圖片就是在784維向量空間裡面的點, 並且擁有比較

複雜的結構 (提醒: 此類資料的視覺化是計算密集型的)。

展平圖片的數字陣列會丟失圖片的二維結構資訊。這顯然是不理想的,最優秀的計算機視覺方法會挖掘並利用這些結構資訊,我們會在後續教程中介紹。但是在這個教程中我們忽略這些結構,所介紹的簡單數學模型,softmax迴歸(softmax regression),不會利用這些結構資訊。

因此,在MNIST訓練資料集中,mnist.train.images 是一個形狀為 [60000, 784] 的張量,第一個維度數字用來索引圖片,第二個維度數字用來索引每張圖片中的畫素點。在此張量裡的每一個元素,都表示某張圖片裡的某個畫素的強度值,值介於0和1之間。

相對應的MNIST資料集的標籤是介於0到9的數字,用來描述給定圖片裡表示的數字。為了用於這個教程,我們使標籤資料是"one-hot vectors"。 一個one-hot向量除了某一位的數字是1以外其餘各維度數字都是0。所以在此教程中,數字n將表示成一個只有在第n維度(從0開始)數字為1的10維向量。比如,標籤0將表示成([1,0,0,0,0,0,0,0,0,0,0])。因此, mnist.train.labels 是一個 [60000, 10] 的數字矩陣。

現在,我們準備好可以開始構建我們的模型啦!

Softmax迴歸介紹

我們知道MNIST的每一張圖片都表示一個數字,從0到9。我們希望得到給定圖片代表每個數字的概率。比如說,我們的模型可能推測一張包含9的圖片代表數字9的概率是80%但是判斷它是8的概率是5%(因為8和9都有上半部分的小圓),然後給予它代表其他數字的概率更小的值。

這是一個使用softmax迴歸(softmax regression)模型的經典案例。softmax模型可以用來給不同的物件分配概率。即使在之後,我們訓練更加精細的模型時,最後一步也需要用softmax來分配概率。

softmax迴歸(softmax regression)分兩步:第一步

為了得到一張給定圖片屬於某個特定數字類的證據(evidence),我們對圖片畫素值進行加權求和。如果這個畫素具有很強的證據說明這張圖片不屬於該類,那麼相應的權值為負數,相反如果這個畫素擁有有利的證據支援這張圖片屬於這個類,那麼權值是正數。

下面的圖片顯示了一個模型學習到的圖片上每個畫素對於特定數字類的權值。紅色代表負數權值,藍色代表正數權值。

我們也需要加入一個額外的偏置量(bias),因為輸入往往會帶有一些無關的干擾量。因此對於給定的輸入圖片 x 它代表的是數字 i 的證據可以表示為

其中代表權重,代表數字 i 類的偏置量,j 代表給定圖片 x 的畫素索引用於畫素求和。然後用softmax函式可以把這些證據轉換成概率 y

這裡的softmax可以看成是一個激勵(activation)函式或者連結(link)函式,把我們定義的線性函式的輸出轉換成我們想要的格式,也就是關於10個數字類的概率分佈。因此,給定一張圖片,它對於每一個數字的吻合度可以被softmax函式轉換成為一個概率值。softmax函式可以定義為:

展開等式右邊的子式,可以得到:

但是更多的時候把softmax模型函式定義為前一種形式:把輸入值當成冪指數求值,再正則化這些結果值。這個冪運算表示,更大的證據對應更大的假設模型(hypothesis)裡面的乘數權重值。反之,擁有更少的證據意味著在假設模型裡面擁有更小的乘數係數。假設模型裡的權值不可以是0值或者負值。Softmax然後會正則化這些權重值,使它們的總和等於1,以此構造一個有效的概率分佈。(更多的關於Softmax函式的資訊,可以參考Michael Nieslen的書裡面的這個部分,其中有關於softmax的可互動式的視覺化解釋。)

對於softmax迴歸模型可以用下面的圖解釋,對於輸入的xs加權求和,再分別加上一個偏置量,最後再輸入到softmax函式中:

如果把它寫成一個等式,我們可以得到:

我們也可以用向量表示這個計算過程:用矩陣乘法和向量相加。這有助於提高計算效率。(也是一種更有效的思考方式)

更進一步,可以寫成更加緊湊的方式:

實現迴歸模型

為了用python實現高效的數值計算,我們通常會使用函式庫,比如NumPy,會把類似矩陣乘法這樣的複雜運算使用其他外部語言實現。不幸的是,從外部計算切換回Python的每一個操作,仍然是一個很大的開銷。如果你用GPU來進行外部計算,這樣的開銷會更大。用分散式的計算方式,也會花費更多的資源用來傳輸資料。

二、TensorFlow 實現手寫字型識別

1.網路結構圖(5大元件)

2、資料輸入設計(Input)

run_training()方法的一開始,input_data.read_data_sets()函式會確保你的本地訓練資料夾中,已經下載了正確的資料,然後將這些資料解壓並返回一個含有DataSet例項的字典。

輸入與佔位符(Inputs and Placeholders)

資料集 目的
data_sets.train 55000個影象和標籤(labels),作為主要訓練集。
data_sets.validation 5000個影象和標籤,用於迭代驗證訓練準確度。
data_sets.test 10000個影象和標籤,用於最終測試訓練準確度(trained accuracy)。
# 準備訓練/驗證/測試資料集
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

# 使用 placeholder 將資料送入網路,None 表示張量的第一個維度可以是任意長度的
with tf.name_scope('Input'):
    X = tf.placeholder(dtype=tf.float32, shape=[None, 784], name='X_placeholder')
    Y = tf.placeholder(dtype=tf.int32, shape=[None, 10], name='Y_placeholder')

在訓練迴圈(training loop)的後續步驟中,傳入的整個影象和標籤資料集會被切片,以符合每一個操作所設定的batch_size值,佔位符操作將會填補以符合這個batch_size值。然後使用feed_dict引數,將資料傳入sess.run()函式。

這裡的xy並不是特定的值,相反,他們都只是一個佔位符,可以在TensorFlow執行某一計算時根據該佔位符輸入具體的值。

輸入圖片x是一個2維的浮點數張量。這裡,分配給它的shape[None, 784],其中784是一張展平的MNIST圖片的維度。None表示其值大小不定,在這裡作為第一個維度值,用以指代batch的大小,意即x的數量不定。輸出類別值y_也是一個2維張量,其中每一行為一個10維的one-hot向量,用於代表對應某一MNIST圖片的類別。

雖然placeholdershape引數是可選的,但有了它,TensorFlow能夠自動捕捉因資料維度不一致導致的錯誤。

3.前向網路設計(Inference)--儘可能地構建好圖表,滿足促使神經網路向前反饋並做出預測的要求。

在訓練迴圈(training loop)的後續步驟中,傳入的整個影象和標籤資料集會被切片,以符合每一個操作所設定的batch_size值,佔位符操作將會填補以符合這個batch_size值。然後使用feed_dict引數,將資料傳入sess.run()函式。

在為資料建立佔位符之後,就可以執行mnist.py檔案,經過三階段的模式函式操作:inference(), loss(),和training()。圖表就構建完成了。

我們現在為模型定義權重W和偏置b。可以將它們當作額外的輸入量,但是TensorFlow有一個更好的處理方式:變數。一個變數代表著TensorFlow計算圖中的一個值,能夠在計算過程中使用,甚至進行修改。在機器學習的應用過程中,模型引數一般用Variable來表示。

with tf.name_scope('Inference'):
    W = tf.Variable(initial_value=tf.random_normal(shape=[784, 10], stddev=0.01), name='Weights')
    b = tf.Variable(initial_value=tf.zeros(shape=[10]), name='bias')
    logits = tf.matmul(X, W) + b
    Y_pred = tf.nn.softmax(logits=logits)

我們在呼叫tf.Variable的時候傳入初始值。在這個例子裡,我們把Wb都初始化為零向量。W是一個784x10的矩陣(因為我們有784個特徵和10個輸出值)。b是一個10維的向量(因為我們有10個分類)。

現在我們可以實現我們的迴歸模型了。這隻需要一行!我們把向量化後的圖片x和權重矩陣W相乘,加上偏置b,然後計算每個分類的softmax概率值。

y = tf.nn.softmax(tf.matmul(x,W) + b)

4.損失函式設計(Loss)---往inference圖表中新增生成損失(loss)所需要的操作(ops)。

首先,labels_placeholer中的值,將被編碼為一個含有1-hot values的Tensor。例如,如果類識別符號為“3”,那麼該值就會被轉換為: [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]

with tf.name_scope('Loss'):
    # 求交叉熵損失
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=logits, name='cross_entropy')
    # 求平均
    loss = tf.reduce_mean(cross_entropy, name='loss')

5.引數學習演算法設計(Optimization)---往損失圖表中新增計算並應用梯度(gradients)所需的操作。

我們生成一個變數用於儲存全域性訓練步驟(global training step)的數值,並函式更新系統中的三角權重(triangle weights)、增加全域性步驟的操作。根據慣例,這個操作被稱為 train_op,是TensorFlow會話(session)誘發一個完整訓練步驟所必須執行的操作

with tf.name_scope('Optimization'):
    optimizer = tf.train.GradientDescentOptimizer(FLAGS.learning_rate).minimize(loss)

6.評估節點設計(Evaluate)

每隔一千個訓練步驟,我們的程式碼會嘗試使用訓練資料集與測試資料集,對模型進行評估。do_eval函式會被呼叫三次,分別使用訓練資料集、驗證資料集合測試資料集。

with tf.name_scope('Evaluate'):
    # 返回驗證集/測試集預測正確或錯誤的布林值
    correct_prediction = tf.equal(tf.argmax(Y_pred, 1), tf.argmax(Y, 1))
    # 將布林值轉換為浮點數後,求平均準確率
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

# eg:[True, True, False, True] 經過資料型別轉換後變成 [1, 1, 0, 1],取平均後得到0.75

7.完整程式碼及結果

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'


# 定義一個全域性物件來獲取引數的值,在程式中使用(eg:FLAGS.iteration)來引用引數
FLAGS = tf.app.flags.FLAGS

# 設定訓練相關引數
tf.app.flags.DEFINE_integer("iteration", 10001, "Iterations to train [1e4]")
tf.app.flags.DEFINE_integer("disp_freq", 200, "Display the current results every display_freq iterations [1e2]")
tf.app.flags.DEFINE_integer("train_batch_size", 100, "The size of batch images [128]")
tf.app.flags.DEFINE_float("learning_rate", 0.1, "Learning rate of for adam [0.01]")
tf.app.flags.DEFINE_string("log_dir", "logs", "Directory of logs.")


def main(argv=None):
    # 0、準備訓練/驗證/測試資料集
    mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

    # 1、資料輸入設計:使用 placeholder 將資料送入網路,None 表示張量的第一個維度可以是任意長度的
    with tf.name_scope('Input'):
        X = tf.placeholder(dtype=tf.float32, shape=[None, 784], name='X_placeholder')
        Y = tf.placeholder(dtype=tf.int32, shape=[None, 10], name='Y_placeholder')

    # 2、前向網路設計
    with tf.name_scope('Inference'):
        W = tf.Variable(initial_value=tf.random_normal(shape=[784, 10], stddev=0.01), name='Weights')
        b = tf.Variable(initial_value=tf.zeros(shape=[10]), name='bias')
        logits = tf.matmul(X, W) + b
        Y_pred = tf.nn.softmax(logits=logits)

    # 3、損失函式設計
    with tf.name_scope('Loss'):
        # 求交叉熵損失
        cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=logits, name='cross_entropy')
        # 求平均
        loss = tf.reduce_mean(cross_entropy, name='loss')

    # 4、引數學習演算法設計
    with tf.name_scope('Optimization'):
        optimizer = tf.train.GradientDescentOptimizer(FLAGS.learning_rate).minimize(loss)

    # 5、評估節點設計
    with tf.name_scope('Evaluate'):
        # 返回驗證集/測試集預測正確或錯誤的布林值
        correct_prediction = tf.equal(tf.argmax(Y_pred, 1), tf.argmax(Y, 1))
        # 將布林值轉換為浮點數後,求平均準確率
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    print('~~~~~~~~~~~開始執行計算圖~~~~~~~~~~~~~~')
    with tf.Session() as sess:
        summary_writer = tf.summary.FileWriter(logdir=FLAGS.log_dir, graph=sess.graph)
        # 初始化所有變數
        sess.run(tf.global_variables_initializer())
        total_loss = 0
        for i in range(0, FLAGS.iteration):
            X_batch, Y_batch = mnist.train.next_batch(FLAGS.train_batch_size)
            _, loss_batch = sess.run([optimizer, loss], feed_dict={X: X_batch, Y: Y_batch})
            total_loss += loss_batch
            if i % FLAGS.disp_freq == 0:
                val_acc = sess.run(accuracy, feed_dict={X: mnist.validation.images, Y: mnist.validation.labels})
                if i == 0:
                    print('step: {}, train_loss: {}, val_acc: {}'.format(i, total_loss, val_acc))
                else:
                    print('step: {}, train_loss: {}, val_acc: {}'.format(i, total_loss/FLAGS.disp_freq, val_acc))
                total_loss = 0

        test_acc = sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels})
        print('test accuracy: {}'.format(test_acc))
        summary_writer.close()

# 執行main函式
if __name__ == '__main__':
    tf.app.run()


# 輸出結果如下:
Extracting MNIST_data\train-images-idx3-ubyte.gz
Extracting MNIST_data\train-labels-idx1-ubyte.gz
Extracting MNIST_data\t10k-images-idx3-ubyte.gz
Extracting MNIST_data\t10k-labels-idx1-ubyte.gz
~~~~~~~~~~~開始執行計算圖~~~~~~~~~~~~~~
step: 0, train_loss: 2.3216300010681152, val_acc: 0.36899998784065247
step: 200, train_loss: 0.750925962626934, val_acc: 0.8835999965667725
......
......
......
......
......
......
step: 9800, train_loss: 0.26842106945812705, val_acc: 0.9269999861717224
step: 10000, train_loss: 0.27616902984678743, val_acc: 0.9254000186920166
test accuracy: 0.9226999878883362

8.構建一個多層卷積網路

在MNIST上只有92%正確率,實在太糟糕。在這個小節裡,我們用一個稍微複雜的模型:卷積神經網路來改善效果。這會達到大概99.2%的準確率。雖然不是最高,但是還是比較讓人滿意。

權重初始化

為了建立這個模型,我們需要建立大量的權重和偏置項。這個模型中的權重在初始化時應該加入少量的噪聲來打破對稱性以及避免0梯度。由於我們使用的是ReLU神經元,因此比較好的做法是用一個較小的正數來初始化偏置項,以避免神經元節點輸出恆為0的問題(dead neurons)。為了不在建立模型的時候反覆做初始化操作,我們定義兩個函式用於初始化。

def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)

def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

卷積和池化

TensorFlow在卷積和池化上有很強的靈活性。我們怎麼處理邊界?步長應該設多大?在這個例項裡,我們會一直使用vanilla版本。我們的卷積使用1步長(stride size),0邊距(padding size)的模板,保證輸出和輸入是同一個大小。我們的池化用簡單傳統的2x2大小的模板做max pooling。為了程式碼更簡潔,我們把這部分抽象成一個函式。

def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                        strides=[1, 2, 2, 1], padding='SAME')

第一層卷積

現在我們可以開始實現第一層了。它由一個卷積接一個max pooling完成。卷積在每個5x5的patch中算出32個特徵。卷積的權重張量形狀是[5, 5, 1, 32],前兩個維度是patch的大小,接著是輸入的通道數目,最後是輸出的通道數目。 而對於每一個輸出通道都有一個對應的偏置量。

W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

為了用這一層,我們把x變成一個4d向量,其第2、第3維對應圖片的寬、高,最後一維代表圖片的顏色通道數(因為是灰度圖所以這裡的通道數為1,如果是rgb彩色圖,則為3)。

x_image = tf.reshape(x, [-1,28,28,1])

We then convolve x_image with the weight tensor, add the bias, apply the ReLU function, and finally max pool. 我們把x_image和權值向量進行卷積,加上偏置項,然後應用ReLU啟用函式,最後進行max pooling。

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

第二層卷積

為了構建一個更深的網路,我們會把幾個類似的層堆疊起來。第二層中,每個5x5的patch會得到64個特徵。

W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

密集連線層

現在,圖片尺寸減小到7x7,我們加入一個有1024個神經元的全連線層,用於處理整個圖片。我們把池化層輸出的張量reshape成一些向量,乘上權重矩陣,加上偏置,然後對其使用ReLU。

W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])

h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

Dropout

為了減少過擬合,我們在輸出層之前加入dropout。我們用一個placeholder來代表一個神經元的輸出在dropout中保持不變的概率。這樣我們可以在訓練過程中啟用dropout,在測試過程中關閉dropout。 TensorFlow的tf.nn.dropout操作除了可以遮蔽神經元的輸出外,還會自動處理神經元輸出值的scale。所以用dropout的時候可以不用考慮scale。

keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

輸出層

最後,我們新增一個softmax層,就像前面的單層softmax regression一樣。

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

訓練和評估模型

這個模型的效果如何呢?

為了進行訓練和評估,我們使用與之前簡單的單層SoftMax神經網路模型幾乎相同的一套程式碼,只是我們會用更加複雜的ADAM優化器來做梯度最速下降,在feed_dict中加入額外的引數keep_prob來控制dropout比例。然後每100次迭代輸出一次日誌。

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
  batch = mnist.train.next_batch(50)
  if i%100 == 0:
    train_accuracy = accuracy.eval(feed_dict={
        x:batch[0], y_: batch[1], keep_prob: 1.0})
    print "step %d, training accuracy %g"%(i, train_accuracy)
  train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

print "test accuracy %g"%accuracy.eval(feed_dict={
    x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})

以上程式碼,在最終測試集上的準確率大概是99.2%。

三、tflearn實現手寫識別

1、資料輸入設計(Input),資料準備

在tflearn中不需要定義佔位符等,只需要把資料按照矩陣的方式輸入即可,極大的簡化了資料維護和輸入過程。

X, Y, testX, testY = mnist.load_data(one_hot=True)

# Model
input_layer = tflearn.input_data(shape=[None, 784], name='input')

2.網路定義

在每個一個層不需要定義Variables、loss等,只需要給定tflearn封裝函式引數就可以,簡單幾行程式碼就可以實現tensorflow大量程式碼實現過程,減少出錯概率和程式設計成本

dense1 = tflearn.fully_connected(input_layer, 128, name='dense1')
dense2 = tflearn.fully_connected(dense1, 256, name='dense2')
softmax = tflearn.fully_connected(dense2, 10, activation='softmax')
regression = tflearn.regression(softmax, optimizer='adam',
                                learning_rate=0.001,
                                loss='categorical_crossentropy')
model = tflearn.DNN(regression)

3.模型訓練驗證

模型訓練和評估,在fix函式中給定資料來源X/Y,給出測試資料集評估資料等引數,呼叫一個函式,省去了tensorflow裡繁瑣定義,tflearn對於初學者簡化非常多,封裝了常用的資料處理方式。

model.fit(X, Y, n_epoch=1,
          validation_set=(testX, testY),
          show_metric=True,
          snapshot_epoch=True, # Snapshot (save & evaluate) model every epoch.
          snapshot_step=500, # Snapshot (save & evalaute) model every 500 steps.
          run_id='model_and_weights')

4.完整程式碼

import tflearn
import tflearn.datasets.mnist as mnist

# MNIST Data
X, Y, testX, testY = mnist.load_data(one_hot=True)

# Model
input_layer = tflearn.input_data(shape=[None, 784], name='input')
dense1 = tflearn.fully_connected(input_layer, 128, name='dense1')
dense2 = tflearn.fully_connected(dense1, 256, name='dense2')
softmax = tflearn.fully_connected(dense2, 10, activation='softmax')
regression = tflearn.regression(softmax, optimizer='adam',
                                learning_rate=0.001,
                                loss='categorical_crossentropy')
# Define classifier, with model checkpoint (autosave)
model = tflearn.DNN(regression, checkpoint_path='model/model.tfl.ckpt')

# Train model, with model checkpoint every epoch and every 200 training steps.
model.fit(X, Y, n_epoch=1,
          validation_set=(testX, testY),
          show_metric=True,
          snapshot_epoch=True, # Snapshot (save & evaluate) model every epoch.
          snapshot_step=500, # Snapshot (save & evalaute) model every 500 steps.
          run_id='model_and_weights')