一、用CNN实现手写数字识别

import tensorflow as tf
import numpy as np
from sklearn.datasets import load_digits

import time
print( time.ctime() )

digits = load_digits()
X_data = digits.data.astype(np.float32)
Y_data = digits.target.astype(np.float32).reshape(-1, 1)

def generatebatch(X, Y, n_examples, batch_size):
    for batch_i in range(n_examples // batch_size):
        start = batch_i * batch_size
        end = start + batch_size
        batch_xs = X[start:end]
        batch_ys = Y[start:end]
        yield batch_xs, batch_ys # 生成每一个batch
        
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()

X_data = scaler.fit_transform(X_data)

from sklearn.preprocessing import OneHotEncoder
Y = OneHotEncoder().fit_transform(Y_data).todense()#todense返回矩阵

#转换为图片格式(batch, height, width, channels)
X = X_data.reshape(-1, 8, 8, 1)

batch_size = 8 #使用MBGD算法,batch_size=8

tf.reset_default_graph()#tf.reset_default_graph函数用于清除默认图形堆栈并重置全局默认图形。
#输入层
tf_X = tf.placeholder(tf.float32, [None, 8, 8, 1])
tf_Y = tf.placeholder(tf.float32, [None, 10])

#卷积层,创建权重变量和偏置变量,其中的四维矩阵代表了filters的深度,即10个filter,其中每个filter的深度是1,若是RGB则每个filter深度为3
conv_filter_w1 = tf.Variable( tf.random_normal([3, 3, 1, 10]) )#3*3的filter,每个filter深度是1,共10个filter
#conv_filter_w1 = tf.get_variable( 'weights', [3, 3, 1, 10], initializer=tf.truncated_normal_initializer(stddev=0.1) )

conv_filter_b1 = tf.Variable( tf.random_normal([10]) )#10个filter对应10个bias
#conv_filter_b1 = tf.get_variable( 'biases', [10], initializer=tf.constant_initializer(0.1) )

#tf.nn.conv2d提供了方便的函数来实现卷积层的前向传播算法
#函数的第一个输入为当前层的节点矩阵,注意这个是四维矩阵即tf_X
#其中(tf_X)后面三维对应一个节点矩阵,第一维对应一个输入batch,比如在输入层
#input[0, :, :, :]表示第一张图,input[1, :, :, :]表示第二张图,input[2, :, :, :]表示第三张图,以此类推
#tf.nn.conv2d第二个参数提供了卷积层权重,第三个参数是不同维度上的步长,长度为4的数组,第一维和最后一维数字要求一定是1
#最后一个参数是padding,SAME表示添加全0填充,VALID表示不添加
conv = tf.nn.conv2d( tf_X, conv_filter_w1, strides=[1, 1, 1, 1], padding='SAME')

#tf.nn.bias_add提供了一个方便的函数给每个节点加上偏置项
#注意这里不能直接使用加法,因为矩阵上不同位置上的节点都需要加上同样的偏置项,也就是下面第二种直接加法是错误的
bias = tf.nn.bias_add( conv, conv_filter_b1 )
#bias = conv + conv_filter_b1这是错误的做法

#ReLU激活函数去线性化
relu_feature_maps1 = tf.nn.relu( bias )
print(relu_feature_maps1)
#Tensor("Relu:0", shape=(?, 8, 8, 10), dtype=float32)

# 池化层,tf.nn.max_pool实现了最大池化层的前向传播过程,参数和tf.nn.conv2d函数类似
#ksize提供了filter的尺寸,strides提供了步长,padding提供是否使用全0填充
max_pool1 = tf.nn.max_pool( relu_feature_maps1, ksize=[1,3,3,1], strides=[1,2,2,1], padding='SAME')

print(max_pool1)
#Tensor("MaxPool:0", shape=(?, 4,4,10), dtype=float32)

# 卷积层2
conv_filter_w2 = tf.Variable( tf.random_normal([3, 3, 10, 5]) )#因为上一层的卷积层深度是10,即有10个输出,这里一个filter深度就是10,而用5个filter
conv_filter_b2 =  tf.Variable( tf.random_normal([5]) )
conv2 = tf.nn.conv2d(relu_feature_maps1, conv_filter_w2, strides=[1, 2, 2, 1], padding='SAME')

conv_out2 = tf.nn.bias_add( conv2, conv_filter_b2 )
print(conv_out2)
#Tensor("BiasAdd_1:0", shape=(?, 4, 4, 5), dtype=float32)

# BN归一化层+激活层
#批标准化(batch normalization,BN)一般用在激活函数之前,使结果各个维度均值为0,方差为1。
#通过规范化让激活函数分布在线性区间,让每一层的输入有一个稳定的分布会有利于网络的训练。
batch_mean, batch_var = tf.nn.moments( conv_out2, axes=[0, 1, 2], keep_dims=True )
#axes:表示在哪个维度上求解,是个list,例如 [0, 1, 2]
#keep_dims:是否保持维度

shift = tf.Variable(tf.zeros([5]))
scale = tf.Variable(tf.ones([5]))
epsilon = 1e-3
#•tf.nn.batch_normalization(x, mean, variance, offset, scale, variance_epsilon, name=None)
BN_out = tf.nn.batch_normalization( conv_out2, batch_mean, batch_var, shift, scale, epsilon )
print(BN_out)
#Tensor("batchnorm/add_1:0", shape=(?, 4, 4, 5), dtype=float32)

relu_BN_maps2 = tf.nn.relu(BN_out)

# 池化层
max_pool2 = tf.nn.max_pool( relu_BN_maps2, ksize=[1,3,3,1], strides=[1,2,2,1], padding='SAME' )
print(max_pool2)
#Tensor("MaxPool_1:0", shape=(?, 2, 2, 5), dtype=float32)

# 将特征图进行展开
max_pool2_flat = tf.reshape( max_pool2, [-1, 2*2*5] )

# 全连接层
fc_w1 = tf.Variable( tf.random_normal([2*2*5,50]) )
fc_b1 =  tf.Variable( tf.random_normal([50]) )
fc_out1 = tf.nn.relu( tf.matmul(max_pool2_flat, fc_w1) + fc_b1 )

# 输出层
out_w1 = tf.Variable( tf.random_normal([50,10]) )
out_b1 = tf.Variable( tf.random_normal([10]) )
pred = tf.nn.softmax( tf.matmul(fc_out1, out_w1) + out_b1 )

loss = -tf.reduce_mean( tf_Y * tf.log( tf.clip_by_value(pred,1e-11,1.0) ) )

train_step = tf.train.AdamOptimizer(1e-3).minimize(loss)

y_pred = tf.arg_max(pred,1)
bool_pred = tf.equal( tf.arg_max(tf_Y,1), y_pred )

accuracy = tf.reduce_mean( tf.cast(bool_pred,tf.float32) ) # 准确率

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    
    for epoch in range(1000): # 迭代1000个周期
        for batch_xs, batch_ys in generatebatch(X, Y, Y.shape[0], batch_size): # 每个周期进行MBGD算法
            sess.run( train_step, feed_dict={tf_X:batch_xs, tf_Y:batch_ys} )
            
        if(epoch%100 == 0):
            res = sess.run(accuracy,feed_dict={ tf_X:X,tf_Y:Y} )
            print (epoch, res)
            
    res_ypred = y_pred.eval(feed_dict={tf_X:X,tf_Y:Y}).flatten() # 只能预测一批样本,不能预测一个样本
    print (res_ypred)
    
from sklearn.metrics import  accuracy_score
print ( accuracy_score( Y_data,res_ypred.reshape(-1,1) ))

print( time.ctime() )