总算是开始了自己的第一篇技术博客。
也总算是大概弄懂了一些深度学习、神经网络、卷积神经网络的知识,能靠自己实现识别字符的卷积神经网络了。
今天时间所剩不多,可能写不完,写到哪算哪吧。
在大二下的《人工智能导论》这门课开始接触深度学习,学得一知半解都算不上,啥都没弄明白,甚至还要做神经网络的实验,本是作为启蒙课的目的也没有达到,反而起到了揠苗助长的效果,让人觉得深度学习、神经网络特别难。就我目前的理解而言,我绝不敢说深度学习、神经网络简单,毕竟现在科技前沿都还没将之完全征服。但是我可以说,理解深度学习、神经网络的基本概念绝对不难。实际上我到目前为止也只是看了吴恩达老师不到半小时的讲解就入门了神经网络,实训课李伟老师一上午左右的讲解也让我掌握了数字图像基本概念和卷积神经网络的思路。不得不抨击一下这门课的设立,我被老师和实验弄迷糊的时间完全可以入门深度学习了,况且还让我很长一段时间对深度学习、神经网络有些畏惧,如今看来,似乎”不过如此“(bushi)。
好吧,啰嗦了半天,好像都没时间写技术性的东西了。
这次的任务是把老师已经用tensorflow1.x写好的识别字符的卷积神经网络改成用tensorflow2.x实现**(封装的特性)**。关于深度学习和神经网络的知识梳理有空再写吧。
先到这里。
————————————————————2022/7/15 23:28
————————————————————2022/7/17 9:41接着写
拖欠了两天都没写完哈哈。不过下一阶段的任务还没开始,也不急。字符识别卷积神经网络搭好了,接下来就是web的搭建了,Python的flask是完全没学过的内容。
直接按照搭建网络的顺序逐个说明吧。
一.数据读取
现在很多CV领域的数据读取都是直接读取现成的数据集,但是这里需要从本地文件当中读取,并进行处理,这一部分tf1和tf2没区别,不用改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def load_data (dir_path ): data = [] labels = [] for item in os.listdir(dir_path): item_path = os.path.join(dir_path, item) if os.path.isdir(item_path): for subitem in os.listdir(item_path): subitem_path = os.path.join(item_path, subitem) gray_image = cv.imread(subitem_path, cv.IMREAD_GRAYSCALE) resized_image = cv.resize(gray_image, (IMAGE_WIDTH, IMAGE_HEIGHT)) data.append(resized_image.ravel()) labels.append(LABEL_DICT[item]) return np.array(data), np.array(labels) ''' data文件夹里面有0~1,A~Z的所有字符,每个字符有一个文件夹存储对应图片,这部分代码的功能就是把每个图片读出来,转换成20*20的大小,label就是文件夹名称 '''
正式进行数据读取的步骤开始有了变化。原本会对训练集和测试集都进行正则化,然后再把标签独热编码,这两步都是自定义的函数。tf2提供相关的API。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 train_data, train_labels = load_data(TRAIN_DIR) train_labels = tf.one_hot(train_labels, 34 ) train_samples_count = len (train_data) train_indicies = np.arange(train_samples_count) np.random.shuffle(train_indicies) test_data, test_labels = load_data(TEST_DIR) test_labels = tf.one_hot(test_labels, 34 ) train_data = tf.reshape(train_data, (train_data.shape[0 ], IMAGE_WIDTH, IMAGE_HEIGHT, 1 )) test_data = tf.reshape(test_data, (test_data.shape[0 ], IMAGE_WIDTH, IMAGE_HEIGHT, 1 )) ''' 可能会问,原先的正则化哪去了,答案是:直接在神经网络入口加一层正则化层就行 这样不用分别处理训练集和测试集,它们进入网络就会正则化了 '''
二.搭建网络(封装的特性)
tf2搭建网络最明显的特点就是封装的特性 ,直接在一个对象当中设置好每一层的参数就可以,不需要手动实现底层代码,有了非常明显的改进。
直接上代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 network = tf.keras.models.Sequential([ tf.keras.layers.BatchNormalization(), tf.keras.layers.Conv2D(filters=32 , kernel_size=5 , activation='relu' , input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, 1 ), padding='same' ), tf.keras.layers.MaxPool2D(pool_size=2 , strides=2 ), tf.keras.layers.Conv2D(filters=64 , kernel_size=5 , activation='relu' , input_shape=(10 , 10 , 32 ), padding='same' ), tf.keras.layers.MaxPool2D(pool_size=2 , strides=2 ), tf.keras.layers.Flatten(), tf.keras.layers.Dense(1024 , activation='relu' ), tf.keras.layers.Dropout(0.3 ), tf.keras.layers.Dense(CLASSIFICATION_COUNT, activation='softmax' ) ])
这只是前向传播的代码,交叉熵损失函数和反向传播算法如下。
1 2 3 network.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3), loss=tf.keras.losses.BinaryCrossentropy(), metrics=['accuracy'])
三.训练模型、评估准确率、保存模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 network.fit(train_data, train_labels, batch_size=50 , epochs=30 , validation_split=0.02 ) ''' 直接指定batch_size和epochs即可,很方便 validation_split这个参数就是每次训练时,把训练集中一定比例的数据不参与训练,用来测算正确率等参数,测出来也不改变参数 最开始validation_split=0.1的时候,训练当中算出来的正确率很高,98/99%甚至100%。但是测试集效果不好,正确率89%左右。我估计是因为直接削减了10%的数据,泛化能力不强。 但是如果设置validation_split=0.0,训练和测试效果很好,98~99%,但是实际检测效果反而不如前者,说明可能过拟合了 所以设置validation_split=0.02,这样中庸的策略既保证了正确率,泛化能力又不错 ''' test_loss, test_acc = network.evaluate(test_data, test_labels) print ('\n测试准确率:' , test_acc)network.save_weights(MODEL_PATH) ''' 保存模型可以直接保存模型。也可以保存模型的权重(参数),预测的时候,构造一个同架构的网络,直接加载参数即可。 前者比较友好,很简单,但是队友用了后者就用后者吧,我觉得后者会更快,因为直接加载参数显然比直接加载模型要更简单 '''
四.实战预测
1.自定义一个加载图片的函数。
1 2 3 4 5 6 def load_image (image_path, width, height ): gray_image = cv.imread(image_path, cv.IMREAD_GRAYSCALE) resized_image = cv.resize(gray_image, (width, height)) data = [] data.append(resized_image.ravel()) return np.array(data)
2.搭建一个网络,并加载权重|参数,这主要是由之前保存只是保存了参数决定的,如果保存的是模型,就直接加载好了。
1 2 3 4 5 6 7 model = tf.keras.models.Sequential([ ]) model.load_weights("model/my_cnn_enu" )
3.读取图片,reshape成可以进入网络的样子
1 2 3 digit_image_path = "images/english.jpg" digit_image = load_image(digit_image_path, IMAGE_WIDTH, IMAGE_HEIGHT) english_image = tf.reshape(digit_image, [-1 , IMAGE_WIDTH, IMAGE_HEIGHT, 1 ])
4.预测
1 2 3 4 5 6 7 results = model.predict(english_image) num = np.argmax(results[0 ]) new_dict = {v : k for k, v in LABEL_DICT.items()} print ("预测结果是:" , new_dict[num])
以上就是全部的内容了,是不是挺简单的呢,反正我觉得挺简单的,哈哈。
让大家看看效果叭。
最后一张预测错惹,没办法,这个A真的很像4。
第一篇技术博客正式完结,实际用时也不久,挺好的。
谢谢观看~