我正在按照这个网站上的代码进行学习:
https://blog.luisfred.com.br/reconhecimento-de-escrita-manual-com-redes-neurais-convolucionais/
以下是网站上逐步讲解的代码:
from keras. datasets import mnistfrom keras. models import Sequentialfrom keras. layers import Densefrom keras. layers import Dropoutfrom keras. layers import Flattenimport numpy as npfrom matplotlib import pyplot as pltfrom keras. layers . convolutional import Conv2Dfrom keras. layers . convolutional import MaxPooling2Dfrom keras. utils import np_utilsfrom keras import backend as KK . set_image_dim_ordering ( 'th' )import cv2import matplotlib. pyplot as plt#% inline matplotlib # 如果你在使用Jupyter,这对在单元格内绘制图形或图表会很有用#将数据分为训练和测试子集( X_train , y_train ) , ( X_test , y_test ) = mnist. load_data ( )# 因为我们在处理灰度图,所以可以# 将深度设置为值1X_train = X_train . reshape ( X_train . shape [ 0 ] , 1 , 28 , 28 ) . astype ( 'float32' )X_test = X_test . reshape ( X_test . shape [ 0 ] , 1 , 28 , 28 ) . astype ( 'float32' )# 我们根据# 灰度图对数据进行归一化。浮点值在范围[0,1]内,而不是[.255]X_train = X_train / 255X_test = X_test / 255# 将y_train和y_test转换为二进制类别数组(one-hot向量),它们是类别向量y_train = np_utils. to_categorical ( y_train )y_test = np_utils. to_categorical ( y_test )# MNIST中发现的数字类型数量。在这种情况下,值为10,对应于(0,1,2,3,4,5,6,7,8,9)。num_classes = y_test. shape [ 1 ]def deeper_cnn_model ( ) : model = Sequential ( ) # Convolution2D将是我们的输入层。我们可以观察到它具有 # 30个特征图,大小为5×5,激活函数类型为ReLU。 model.add ( Conv2D ( 30 , ( 5 , 5 ) , input_shape = ( 1 , 28 , 28 ) , activation = 'relu' ) ) # MaxPooling2D层将是我们的第二层,我们将有一个2x2大小的采样窗口 model.add ( MaxPooling2D ( pool_size = ( 2 , 2 ) ) ) # 一个新的卷积层,具有15个大小为3×3的特征图,激活函数为ReLU model.add ( Conv2D ( 15 , ( 3 , 3 ) , activation = 'relu' ) ) # 一个新的2x2维度的子采样 model.add ( MaxPooling2D ( pool_size = ( 2 , 2 ) ) ) # 我们包含一个20%的概率的dropout(你可以尝试其他值) model.add ( Dropout ( 0.2 ) ) # 我们需要转换卷积层的输出,以便它可以作为下一个密集连接层的输入。 # 这做的就是“展平/flatten”卷积层的输出的结构,创建一个单一的长特征向量 # 供Fully Connected层使用。 model.add ( Flatten ( ) ) # 具有128个神经元的全连接层。 model.add ( Dense ( 128 , activation = 'relu' ) ) # 接着是一个新的具有64个神经元的全连接层 model.add ( Dense ( 64 , activation = 'relu' ) ) # 接着是一个新的具有32个神经元的全连接层 model.add ( Dense ( 32 , activation = 'relu' ) ) # 输出层具有与 # 要获得的类别数量兼容的神经元数量。请注意我们使用的是softmax激活函数, model.add ( Dense ( num_classes, activation = 'softmax' , name = 'preds' ) ) # 配置神经网络的整个训练过程 model.compile ( loss = 'categorical_crossentropy' , optimizer = 'adam' , metrics = [ 'accuracy' ] ) return modelmodel = deeper_cnn_model ( )model.summary ( )model.fit ( X_train , y_train, validation_data = ( X_test , y_test ) , epochs = 10 , batch_size = 200 )scores = model. evaluate ( X_test , y_test, verbose = 0 )print ( "\ nacc:% .2f %%" % (scores [1] * 100))###在训练完成后增强以检查多个数字img_pred = cv2. imread ( 'five.JPG' , 0 )plt.imshow(img_pred, cmap='gray')# 强制图像具有与训练数据相同的输入尺寸(28x28)if img_pred. shape != [ 28 , 28 ] : img2 = cv2. resize ( img_pred, ( 28 , 28 ) ) img_pred = img2. reshape ( 28 , 28 , - 1 ) ;else : img_pred = img_pred. reshape ( 28 , 28 , - 1 ) ;# 这里我们也告知深度的值为1,行数和列数,对应于图像的28x28。img_pred = img_pred. reshape ( 1 , 1 , 28 , 28 )pred = model. predict_classes ( img_pred )pred_proba = model. predict_proba ( img_pred )pred_proba = "% .2f %%" % (pred_proba [0] [pred] * 100)print ( pred [ 0 ] , "的概率为" , pred_proba )
在最后,我尝试对自己绘制并导入的数字五进行预测(我也尝试了其他手绘数字,结果同样不理想):
img_pred = cv2. imread ( 'five.JPG' , 0 )plt.imshow(img_pred, cmap='gray')# 强制图像具有与训练数据相同的输入尺寸(28x28)if img_pred. shape != [ 28 , 28 ] : img2 = cv2. resize ( img_pred, ( 28 , 28 ) ) img_pred = img2. reshape ( 28 , 28 , - 1 ) ;else : img_pred = img_pred. reshape ( 28 , 28 , - 1 ) ;# 这里我们也告知深度的值为1,行数和列数,对应于图像的28x28。img_pred = img_pred. reshape ( 1 , 1 , 28 , 28 )pred = model. predict_classes ( img_pred )pred_proba = model. predict_proba ( img_pred )pred_proba = "% .2f %%" % (pred_proba [0] [pred] * 100)print ( pred [ 0 ] , "的概率为" , pred_proba )
这是five.jpg的外观:
但是当我输入自己的数字时,模型预测错误。你认为这可能是什么原因?我承认我对机器学习是新手,刚刚开始尝试。我的想法是可能是图像的居中或图像的归一化有问题?任何帮助都非常感激!
编辑1:
MNIST测试数字看起来像这样:
回答:
看起来你有两个问题,正如你所怀疑的,这些问题与数据的预处理有关。
第一个问题是你的图像相对于训练数据是颠倒的:
- 在用
img_pred = cv2. imread ( 'five.JPG' , 0 )
读取你的.jpg的一个通道后,背景像素接近白色,值在215-238附近。 - 如果你查看
X_train
中的训练数据,背景像素全部为零,数字为白色或接近白色(上限210-255)。
尝试将你的图像与X_train
中的一些选择并排绘制,你会发现它们是颠倒的。
另一个问题是cv2.resize()
中的默认插值不保留数据的缩放。在你调整数据大小后,最小值跳到60,而不是0。比较调整尺寸前后的img.pred.min()
和img.pred.max()
的值。
你可以使用以下函数来颠倒并缩放你的数据,使其看起来更像MNIST输入数据:
def mnist_bytescale(image): # 使用float进行重新缩放 img_temp = image.astype(np.float32) # 重新归零数据 img_temp -= img_temp.min() # 重新缩放并颠倒 img_temp /= (img_temp.max()-img_temp.min()) img_temp *= 255 return 255 - img_temp.astype('uint')
这将翻转你的数据,并线性缩放到0到255之间,就像网络训练的数据一样。然而,如果你绘制mnist_bytescale(img_pred)
,你会注意到大多数像素的背景水平仍然不是完全0,因为你原始图像的背景水平不是恒定的(可能是由于JPEG压缩)。如果你的网络仍然对这些翻转和缩放的数据有问题,你可以尝试使用np.clip将背景水平置零,看看是否有帮助。