找回密码
 立即注册
搜索

聊聊图像识别的小原理,动手实现自己的图像分类

牙雅 2024-4-29 22:45:34 显示全部楼层 阅读模式
咱们开门见山,实话实说。



虽然ChatGPT带火了人工智能,但它还没找好挣钱的门路。急得投资人微软充当OpenAI的销售,大夏天的提着2.5L的矿泉水,背着电脑包,到处下基层去跟人聊行业结合,谈产品落地。
镜头一转,在计算机视觉(Computer Vision, CV)领域,人工智能反而挣着钱了。大家用AI生成衣服图片替换模特,也用AI生成游戏怪物替换特效师。甚至在传统的图书出版行业,有些书籍的插画,也开始用AI去生成。
因此,我打算聊聊人工智能在视觉方面的原理。然后,顺手从0到1创建一个属于自己的图像分类开放能力。
一、看看效果

为了让大家直观感受到AI对视觉处理的能力,我先拿一个低成本的图像分类代码演示一下。后面,咱再讲原理并自己建设。
from transformers import AutoImageProcessor, ResNetForImageClassificationimport torchfrom PIL import Image# 加载模型权重processor = AutoImageProcessor.from_pretrained("model")model = ResNetForImageClassification.from_pretrained("model")# 选择一个图片image = Image.open("pics/dog.jpeg")inputs = processor(image, return_tensors="pt")with torch.no_grad():    logits = model(**inputs).logitspredicted_label = logits.argmax(-1).item()print(model.config.id2label[predicted_label])上面利用transformers加载了一个训练好的权重,实现了一个图像分类的功能。这就是全部的python代码。
我的项目文件结构是这样的:
|---- model # 模型权重     |---- cofig.json    |---- preprocessor_config.json    |---- pytorch_model.bin |---- pics # 测试图片    |---- dog.jpeg # 随便一张狗的图片|---- main.py # python代码文件model文件夹下,是人家训练好的模型,我们直接拿来用。你可以下载pytorch的权重,也可以选择TensorFlow的权重。记得对应pip install一下它们。另外别忘了 pip install transformers
下载地址是https://huggingface.co/microsoft/resnet-50。这是微软的resnet-50模型,它是从COCO 2017数据集中找了1000类图片进行训练的产物。这些分类对应model/config.json中的id2label



我找了一些动物的图片来尝试。我原以为它仅能区分出猫、狗、鼠、兔。没想到,它居然还能进一步识别具体的品种。



如上图,它能识别出是狗,而且是金毛犬。他能识别出是松鼠,还是狐狸松鼠。我又拿哈士奇和狼试了试,结果几毫秒就出正确结果了。
挺神奇!怪不得图像领域能赚钱,这玩意儿确实有用哇。
那么问题来了,它的原理是什么?又是如何实现的呢?
二、讲讲原理

2.1 图像的构成

你有没有想过这样一个问题:
煤炭是黑的,因它本身就是黑的。馒头是白的,因它本来就是白的。但显示器、电视,为啥一会儿黑,一会儿白,有时候还五彩斑斓。色彩和图像究竟是如何被定义的呢?
想明白这个问题,有助于你理解计算机视觉。
我们都知道(看完就知道了),红(Red)、绿(Green)、蓝(Blue)是三原色,又称RGB
这三种颜色相互交融,可调合出世间的五彩斑斓。



很有意思,色彩学和数学扯上了关系。红色的RGB(255, 0, 0), 绿色的RGB(0, 255, 0)。红色混合绿色是黄色,黄色的RGB(255, 255, 0)。如你所料,红绿蓝混在一起,就是(255, 255, 255),它是白色。
基于这个原理,我们才可以用三组数据表示一个色彩点。同时,我们又可以用多个色彩点表示一幅图。这一点,我们通过显示器的屏幕就能看得出来。



你的电脑、手机的显示器,就是由很多个小像素格子组成的。只是它太小太密集,你看不出来。如果,你往屏幕上滴一滴水,就会发现其中的奥秘。
至此,我想表达,我们可以用数字来描述任何一幅图。它无非就是横竖(宽高)有多少个点,每个点是什么颜色(RGB的数值)。



那么对这些个数据,我们能做什么呢?
2.2 聊透卷积神经网络的原理

提到图像处理,不得不提卷积神经网络(Convolutional Neural Network, CNN)。它构建起一个层级化的模型,可谓是经典中的经典。
CNN有三板斧:卷积层(Convolutional Layer)、池化层(Pooling Layer)和全连接层(Fully Connected Layer)。
一般的文章提到卷积层,大多会放出这样的图。



并且配上文字解释说:小绿方块是个3×3的卷积核,在淡蓝色5×5像素的图上以1为步长行进。在不填充的情况下,最大能走3步。所以,最终形成右侧3×3的结果。这个卷积核,就是一个特征提取器,能过滤它所关注的数据特征。
这……说得没错。但对初学者来说,还是很抽象:它怎么就学会过滤特征了?它又过滤了什么样的特征?
首先,做卷积运算应该不难理解。下面这幅图,演示了卷积运算。



这是一个3×3的卷积核。它每个格子都带参数。从图像上卷过之后,将图上的像素和自己进行运算,最终输出卷积后的数据。上面的卷积结果让轮廓更加明显。
卷积层在参与训练时,它的参数是可变的。它会通过学习来改变自己的数值。它先随机猜测一个特征,比如只要中心点的像素,其它全都×0变为空白。在验证的过程中,如果猜对了,固然挺好。如果猜不对,那么再进行改变,直到接近正确答案。
一套神经网络,会有好多个卷积层。每个卷积层,又会有好多个卷积核。这样就可以从不同的维度来收集一张图片的特征。甚至说,还可以前面卷完了后边接着卷。
我们看下面这张经典的VGG-19的结构图。3×3 conv,64表示有643×3的卷积核在一张图片上提取信息。后面还有128个、256个。



卷积就像是提取一个学生的成绩。前面卷数学计算,中间卷体能指标,后边卷文学修养。甚至在文学大类中,再提取关于诗人“李白”的专项考核。总之就是要找差别,要靠特征来判定你是不是优秀学生。不得不说卷积的“卷”,翻译得很到位。
经过训练,模型合格,每个卷积核都会得到合适的参数值。这就好比对于评价学生,我们先猜了一个自认为靠谱的方案。然后经过多轮尝试和调整,最终形成了一套量化标准。
那么,卷积层的参数能看吗?都说AI是黑盒,不知道它发生了什么!这句话虽然没错,但在一些简单的场景,还不至于上升到玄学的境界。
模型权重(最终训练出来的那玩意儿)定型后,会体现在模型参数的数值上。整个网络有多少层结构,每层的数值是多少,其实能打印,不过全是向量,你看不出啥道道。但是,如果我们生成噪点图像,让图像的特征向卷积核的权重靠拢,去极大化这个特征,那么或许可以有些收获。
下面,我解剖一个图片分类网络结构的权重。我选择几个层,然后极大化这些数值,也输出了一些视觉的结果。这个操作稍微有点专业,因此我仅摆出来效果,期望帮助大家从视觉上去加深理解。请原谅我不去细讲它。



我并没有拿生物书上的细胞照片来忽悠您,尽管这两者有点像。这确实是将神经网络的权重碎片“反编译”成图片的结果。可能这个结果,让那些持有“生物就是计算机程序”这一观点的人很兴奋。
其实有些图片也并不抽象,或多或少也能推测出一些特征信息。



看来这个卷积层的意义就是对图像进行特征提取。它通过撒出大量的卷积核,去挖掘一张图中存在的独特边缘或是纹理,从而给下游分类任务提供判断依据。
这一层接一层地卷积,是不是太卷了?不累吗?
设计者也考虑到了这个问题,因此引入了池化层(Pooling layer)的概念。
池化层可以在一定程度上解决过“卷”的问题!其手段就类似于灭霸的响指,会让样本中一半的像素消失。看下面这个图。



上图采用的是最大池化(Max Pooling)。一个2×2的池化窗口,在图片上行进。它所经过的地方,取一个窗口内最大的数值作为输出。
按照这个配置,它可以将一张80×80的样本缩小到40×40。而且你看上面的图,即便是缩小后,特征也不变,还能看出来本来的轮廓。如果不嫌麻烦,你再往上翻找到那张VGG-19的结构图。output size开始是224,经过pool,/2之后就少了一半,变成了112。这就是卷积起的作用。
在算力不变的情况下,数据量成半砍,那干活肯定快,空出的时间可以去休闲。所以我说它“反卷”。
在训练时,卷积层的卷积核参数会反复调整并矫正。而池化层不用动脑子,没东西参与训练,要么找一个最大值,要么找一个平均值(平均池化, Average Pooling)。安逸得很!
池化层一般搭配着卷积层使用。这似乎是程序员在对外宣传:人生要间隔着“卷一程”、“躺一程”。



经过神经网络的层层提取,到后期基本上就浓缩出了大量的样本特征。此时通过全连接层(Fully Connected Layer)进行裁决。它是组委会的决赛评委。它的工作简单且重要。前面甭管多少层,都是在收集特征,就像是上报学生的证书、成绩、荣誉、行为、美德、习惯等数据。到全连接层这里,会对特征数据进行加权求和并评分,给出最终结果。
好了,卷积神经网络的组成,我们就讲完了。下面,我们就自己去攒一个网络去实现图像分类。
三、具体实现

我想训练个识别天气照片的分类模型。我们的天气一般分为三类:晴天、多云、有雨。
因此,我找到了一些相关的图片数据,作为数据集。
图片样例如下:



将图片整理成如下的目录结构:
mian.py 程序文件[datasets] 数据集文件夹   |--- [cloudy] 多云   |--- [rain] 有雨   |--- [shine] 晴天于是,我们要训练的图片是3类:cloudyrainshine。我们训练完,让模型对一张陌生图片进行分类,也会是其中之一。就算不像,结果也得是:分类 cloudy, 得分 0.01。这就是分类问题。除此之外还有一个回归问题,它能给出无限的结果,比如明天的股市数据。
# 分类名称names = ['cloudy', 'rain', 'shine'] 有了图片数据,就有了种子。趁着热乎劲儿,我们先来搭建卷积神经网络。
本次选择TensorFlow作为AI框架。我的原则是小数据量用TensorFlow,大数据量用Torch
以下代码运行环境:python 3.9, tensorflow 2.6
3.1 神经网络结构

首先导入tensorflow的包。然后构建一个卷积神经网络。
import tensorflow as tffrom tensorflow.keras import layersfrom tensorflow.keras.models import Sequentialmodel = Sequential([  layers.Rescaling(1./255, input_shape=(200, 200, 3)),  layers.Conv2D(16, 3, padding='same', activation='relu'),  layers.MaxPooling2D(),  layers.Conv2D(32, 3, padding='same', activation='relu'),  layers.MaxPooling2D(),  layers.Conv2D(64, 3, padding='same', activation='relu'),  layers.MaxPooling2D(),  layers.Dropout(0.2),  layers.Flatten(),  layers.Dense(128, activation='relu'),  layers.Dense(3)])前面我们已经学过了卷积神经网络的结构。它的入口是图片数据,后面是卷积层和池化层循环交替。随后,接上全连接层,直达分类的输出。
我们看上面的代码也是这样。主体是卷积层Conv2D搭配着池化层MaxPooling2D。最上层的input_shape=(200, 200, 3)是图片输入的尺寸,表示200×200像素,3代表RGB三通道。到最后Dense(3)输出3个分类。这就是我们设计的网络。
里面的多少个卷积核,每个是几×几,这个我们自己定,就跟搭积木似的,调这些玩意儿才是最终乐趣。
有两点需要说明一下:

  • Dropout(0.2)可以不用。这才是灭霸的响指,它是真的随机断掉20%的参数。目的是避免过拟合。你想啊,剩下的80%都能预测准确,那么它的通用性肯定差不了。
  • Flatten()也叫“拉平层”。我们输入的图片以及后面的卷积池化,都是2维的,有宽有高。但最终我们要输出3分类,这是1维的。因此通过拉平层把2维拉成1维。即:[[1,2],[3,4]]->[1,2,3,4]。方便后面计算。
神经网络的搭建就这么简单。请记住我们已经有了一个model,就是刚才通过Sequential实例化出来的,后面还会用到。
3.2 训练数据

有了神经网络。下面要将数据交给它去训练。通过训练,会把模型里的每个层的模型参数给练出来。于是,它才能实现从量变到质变,拥有智慧,实现自我分类。
# 加载训练数据train_ds = tf.keras.utils.image_dataset_from_directory(  "datasets", image_size=(200, 200), batch_size=24)我们已经准备好训练的图片文件夹datasets了。现在就加载这些数据。并且把里面的图片都规范成image_size=(200, 200)batch_size=24是把数据以24个化为一组,不然太大吃不下。
如果你执行上面的代码的话,控制台会打印如下内容:
Found 768 files belonging to 3 classes.它说找到了768个文件,属于3个分类。这正好对应我的datasets文件夹下3个子文件夹和768张图片。
随后,给模型model配置一下,然后启动训练。
model.compile(optimizer='adam',     loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),     metrics=['accuracy'])cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath="tf2/checkpoint", save_weights_only=True)model.fit(train_ds, epochs=10, callbacks=[cp_callback])model.compile是配置优化器选adam。其余配置损失函数,衡量指标,这些记住就可以。对初学来说,一般不会变。
ModelCheckpoint指定了权重保存的策略,也就是训练结果保存路径是tf2/checkpoint,只保存权重。
model.fit这一行代码就是说对train_ds数据进行训练,训练10个轮次,训练结果回调权重保存策略。
如果执行正常的话,会有如下打印:
Epoch 1/1032/32 [==========] - loss: 0.8752 - accuracy: 0.6589Epoch 2/1032/32 [==========] - loss: 0.4711 - accuracy: 0.8255Epoch 3/1032/32 [==========] - loss: 0.3381 - accuracy: 0.8854Epoch 4/10……Epoch 9/1032/32 [==========] - loss: 0.1136 - accuracy: 0.9583Epoch 10/1032/32 [==========] - loss: 0.0623 - accuracy: 0.9831训练10Epoch。总共768张图,每个batch是24,所以需要768/24=32次才能遍历一次。
accuracy是准确率,随着Epoch增加,准确率已经到了98%以上。
每一个Epoch结束后,你去看tf文件夹下,会生成模型的权重。
mian.py[datasets][tf] 权重文件夹   |--- checkpoint   |--- checkpoint.data-00000-of-00001   |--- checkpoint.index好吧,这就训练完了!
3.3 预测数据

我从网上搜到这么一张图片,这是模型从未见过的图片。



我们来预测一下,它会属于哪个分类。
# 加载模型model.load_weights("tf2/checkpoint")  # 导入图片img = tf.keras.utils.load_img("test.png", target_size=(200, 200))img_array = tf.keras.utils.img_to_array(img)img_array = tf.expand_dims(img_array, 0)# 进行预测predictions = model.predict(img_array)score = tf.nn.softmax(predictions[0])# 输出分类print( "分类 {}, 得分 {:.2f}".format(names[np.argmax(score)], 100*np.max(score)))上面代码是利用tensorflow加载图片,并转换为模型需要的格式。调用model.predict可以进行预测。最终结果是一批独热数据,通过tf.nn.softmax可以读出来。
如果一切正常,控制台会输出:
1/1 [==========] - 0s 171ms/step分类 cloudy, 得分 78.98是的,这张图确实是多云。看来,模型还是挺有作用的。
四、小总结

最后,我给大家布置个作业。虽然文中讲解的内容已经够用。但我还是将更加健壮的代码上传到了Github。地址是 https://github.com/hlwgy/juejin_cnn_img_class/
里面涉及到了一些细节,比如数据缓存、验证集、训练曲线等。大家可以查看完整代码,去了解更细致的知识。



很棒,我讲完了,您听完了。您现在可以训练自己的数据,去实现自己的业务。不用花钱买API接口,想怎么玩就怎么玩!
关键是,它也不复杂呀!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

大神点评18

anda315 2024-4-29 22:46:31 显示全部楼层
听懂了一点。
回复

使用道具 举报

Residualdream 2024-4-29 22:46:39 显示全部楼层
转发了
回复

使用道具 举报

星xīn 2024-4-29 22:46:49 显示全部楼层
转发了
回复

使用道具 举报

飞而复来号 2024-4-29 22:47:20 显示全部楼层
转发了
回复

使用道具 举报

转发了
回复

使用道具 举报

rtrtaa 2024-4-29 22:48:42 显示全部楼层
转发了
回复

使用道具 举报

景丿丿丿 2024-4-29 22:48:53 显示全部楼层
转发了
回复

使用道具 举报

jinihy 2024-4-29 22:49:10 显示全部楼层
转发了
回复

使用道具 举报

博岳通信 2024-4-29 22:50:04 显示全部楼层
转发了
回复

使用道具 举报

高级模式
B Color Image Link Quote Code Smilies