【药智甄选】PaddlePaddle3.0助力实现ResNet50中药分拣
本文介绍基于ResNet50网络判断中药炮制饮片质量的研究。使用成都中医药大学提供的蒲黄、山楂、王不留行3个品种,各含生品、不及、适中、太过4种状态的共6097张图片数据集,经预处理后划分训练集和测试集。构建含预训练ResNet50和全连接层的模型,用Adam优化器等训练,最终准确率达97.3%,实现炮制经验智能化传承。

ResNet50中药炮制饮片质量判断
背景介绍
中药炮制是根据中医药理论,依照临床辨证施治用药的需要和药物自身性质,以及调剂、制剂的不同要求,将中药材制备成中药饮片所采取的一项制药技术。平时我们老百姓能接触到的中药,主要指自己回家煎煮或者请医院代煎煮的中药,都是中药饮片,也就是中药炮制这个技术的结果。而中药炮制饮片,大部分涉及到水火的处理,一定需要讲究“程度适中”,炮制火候不够达不到最好药效,炮制火候过度也会丧失药效。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
“生品”一般是指仅仅采用简单净选得到的饮片,通常没有经过火的处理,也是后续用火加工的原料。“不及”就是“炮制不到位”,没有达到规定的程度,饮片不能发挥最好的效果。“适中”是指炮制程度刚刚好,正是一个最佳的炮制点位,也是通常炮制结束的终点。“太过”是指炮制程度过度了,超过了“适中”的最佳状态,这时候的饮片也会丧失药效,不能再使用了。过去的炮制饮片程度的判断,都是采用的老药工经验判断,但随着老药工人数越来越少,这种经验判断可能存在“失传”的风险。而随着人工智能的发展,使用深度神经网络模型对饮片状态进行判断能达到很好的效果,可以很好的实现经验的“智能化”和经验的传承。本产品基于PaddlePaddle3.0通过使用ResNet50网络进行中药分拣,对饮片状态进行自动判断,实现经验的“智能化”和经验的传承。
数据准备
数据集介绍
我们使用“中药炮制饮片”数据集,该数据集由成都中医药大学提供,共包含中药炮制饮片的 3 个品种,分别为:蒲黄、山楂、王不留行,每个品种又有着4种炮制状态:生品、不及适中、太过,每类包含 500 张图片共12类5000张图片,图片格式为 jpg。 下面是数据集中的一些样例图片:
In [ ]!unzip data/data298711/images.zip -d work/images登录后复制
单例数据可视化查看
In [40]import paddlefrom paddle.io import Datasetimport matplotlib.pyplot as pltimport PIL.Image as Imagepath='/home/aistudio/work/images/ph_bj/IMG_1762.JPG'img = Image.open(path)plt.imshow(img) #根据数组绘制图像plt.show() #显示图像print(img.size)登录后复制
登录后复制
(1000, 1000)登录后复制
数据集整理
本部分地建立了图像文件路径与其对应标签索引之间的映射关系,并将数据集打乱以消除顺序相关性,为后续的训练准备了去序列化的样本数据;
同时通过打印输出,确认了样本数据的有效性和数量;
由于平台字体问题,无法正确显示中文,这里给出英文标签对应的类别:
ph-sp:蒲黄-生品ph_bj:蒲黄-不及ph_sz:蒲黄-适中ph_tg:蒲黄-太过sz_sp:山楂-生品sz_bj:山楂-不及sz_sz:山楂-适中sz_tg:山楂-太过wblx_sp:王不留行-生品wblx_bj:王不留行-不及wblx_sz:王不留行-适中wblx_tg:王不留行-太过In [54]#以下代码用于建立样本数据读取路径与样本标签之间的关系import osimport randomdata_list = [] #用个列表保存每个样本的读取路径、标签#获取work目录下的所有子目录名称,保存进一个列表之中class_list = os.listdir("/home/aistudio/work/images")class_label_list=['ph_bj','ph_sp','ph_sz','ph_tg','sz_bj','sz_sp','sz_sz','sz_tg','wblx_bj','wblx_sp','wblx_sz','wblx_tg']for each in class_list: for f in os.listdir("/home/aistudio/work/images/"+each): label=each label_index=class_label_list.index(label) # print(label,label_index) data_list.append(["/home/aistudio/work/images/"+each+'/'+f,label_index])#按文件顺序读取,可能造成很多属种图片存在序列相关,用random.shuffle方法把样本顺序彻底打乱。random.shuffle(data_list)#打印前十个,可以看出data_list列表中的每个元素是[样本读取路径, 样本标签]。print(data_list[0:10])#打印样本数量,一共有2340个样本。print("样本数量是:{}".format(len(data_list)))登录后复制 [['/home/aistudio/work/images/ph_bj/IMG_20241212_203008.webp', 0], ['/home/aistudio/work/images/ph_sp/IMG_20241212_194947.JPG', 1], ['/home/aistudio/work/images/ph_sz/IMG_3606.JPG', 2], ['/home/aistudio/work/images/sz_tg/IMG_5979.JPG', 7], ['/home/aistudio/work/images/wblx_sp/IMG_20240220_151228.webp', 9], ['/home/aistudio/work/images/wblx_tg/IMG_20240220_112000.webp', 11], ['/home/aistudio/work/images/sz_sp/IMG_5390.JPG', 5], ['/home/aistudio/work/images/wblx_sp/IMG_20240220_103938_3.JPG', 9], ['/home/aistudio/work/images/sz_sz/IMG_3225.JPG', 6], ['/home/aistudio/work/images/ph_tg/IMG_3983.JPG', 3]]样本数量是:6097登录后复制
数据集可视化展示
本部分操作通过计算每个类别的样本数量并使用matplotlib库绘制条形图,直观展示了数据集中各类别的分布情况;
首先统计了每个标签的频次,然后创建了一个条形图,其中横轴表示标签,纵轴表示对应的样本数量;
import matplotlib.pyplot as plt# 计算每个标签的样本数量label_counts = {}for _, label_index in data_list: if label_index in label_counts: label_counts[label_index] += 1 else: label_counts[label_index] = 1# 将标签索引转换回标签名称label_names = class_label_listlabel_counts = {label_names[key]: value for key, value in label_counts.items()}# 创建条形图plt.figure(figsize=(10, 8))plt.bar(label_counts.keys(), label_counts.values(), tick_label=label_counts.keys())plt.xlabel('Label')plt.ylabel('Count')plt.title('Dataset Distribution')plt.xticks(rotation=45)plt.show()登录后复制 数据迭代器构建
本部分代码先是定义了一个数据预处理函数和一个自定义的数据读取器类,用于构造数据读取器和进行数据预处理;
通过Reader类,将数据集划分为训练集和测试集,并实现了数据的加载、预处理和标签转换,输出处理后的图像和标签供模型训练和评估使用;
同时,打印了训练集和测试集的大小以及一个样本的图像形状和标签,验证了数据读取器的正确性;
In [55]#以下代码用于构造读取器与数据预处理#首先需要导入相关的模块import paddlefrom paddle.vision.transforms import Compose, ColorJitter, Resize,Transpose, Normalizeimport cv2import numpy as npfrom PIL import Imagefrom paddle.io import Dataset# 自定义的数据预处理函数,输入原始图像,输出处理后的图像,可以借用paddle.vision.transforms的数据处理功能def preprocess(img): transform = Compose([ Resize(size=(224, 224)), # 把数据长宽像素调成224*224 Normalize(mean=[127.5, 127.5, 127.5], std=[127.5, 127.5, 127.5], data_format='HWC'), # 标准化 Transpose(), # 原始数据形状维度是HWC格式,经过Transpose,转换为CHW格式 ]) img = transform(img).astype("float32") return img# 自定义数据读取器class Reader(Dataset): def __init__(self, data, is_val=False): super().__init__() # 在初始化阶段,把数据集划分训练集和测试集。 # 由于在读取前样本已经被打乱顺序,取20%的样本作为测试集,80%的样本作为训练集。 self.samples = data[-int(len(data)*0.2):] if is_val else data[:-int(len(data)*0.2)] def __getitem__(self, idx): #处理图像 img_path = self.samples[idx][0] #得到某样本的路径 img = Image.open(img_path) if img.mode != 'RGB': img = img.convert('RGB') img = preprocess(img) #数据预处理--这里仅包括简单数据预处理,没有用到数据增强 #处理标签 label = self.samples[idx][1] #得到某样本的标签 label = np.array([label],dtype='int64') #把标签数据类型转成int64 return img, label def __len__(self): #返回每个Epoch中图片数量 return len(self.samples)#生成训练数据集实例train_dataset = Reader(data_list, is_val=False)#生成测试数据集实例eval_dataset = Reader(data_list, is_val=True)#打印一个训练样本print(len(train_dataset)) # 1872#打印一个测试样本print(len(eval_dataset)) # 234print(train_dataset[1136][0].shape)print(train_dataset[1136][1])print(type(train_dataset[1136][1]))登录后复制 48781219(3, 224, 224)[11]登录后复制
模型训练
网络简介
网络背景
ResNet50网络是2015年由微软实验室的何恺明提出,获得ILSVRC2015图像分类竞赛第一名。在ResNet网络提出之前,传统的卷积神经网络都是将一系列的卷积层和池化层堆叠得到的,但当网络堆叠到一定深度时,就会出现退化问题。下图是在CIFAR-10数据集上使用56层网络与20层网络训练误差和测试误差图,由图中数据可以看出,56层网络比20层网络训练误差和测试误差更大,随着网络的加深,其误差并没有如预想的一样减小。
ResNet网络提出了残差网络结构(Residual Network)来减轻退化问题,使用ResNet网络可以实现搭建较深的网络结构(突破1000层)。论文中使用ResNet网络在CIFAR-10数据集上的训练误差与测试误差图如下图所示,图中虚线表示训练误差,实线表示测试误差。由图中数据可以看出,ResNet网络层数越深,其训练误差和测试误差越小。
网络特色
残差网络结构
残差结构是ResNet网络中最重要的结构,其结构图如下图所示,残差网络由两个分支构成:一个主分支,一个shortcuts(图中弧线表示)。主分支通过堆叠一系列的卷积操作得到,shortcuts从输入直接到输出,主分支的输出与shortcuts的输出相加后通过Relu激活函数后即为残差网络最后的输出。
残差网络结构主要由两种,一种是Building Block,适用于较浅的ResNet网络,如ResNet18和ResNet34;另一种是Bottleneck,适用于层数较深的ResNet网络,如ResNet50、ResNet101和ResNet152。
Building Block
Building Block结构图如下图所示,主分支有两层卷积网络结构:
主分支第一层网络以输入channel为64为例,首先通过一个3×3的卷积层,然后通过Batch Normalization层,最后通过Relu激活函数层,输出channel为64;
主分支第二层网络的输入channel为64,首先通过一个3×3的卷积层,然后通过Batch Normalization层,输出channel为64。
最后将主分支输出的特征矩阵与shortcuts输出的特征矩阵相加,通过Relu激活函数即为Building Block最后的输出。
网络定义
定义一个基于ResNet50的自定义神经网络模型,其中利用了预训练的ResNet50作为特征提取器;
由于本任务为12分类,因此添加了一个全连接层以适应12个类别的分类任务;
模型的前向传播方法定义了数据如何通过这些层,实现了从输入到输出的计算过程;
In [74]#定义模型class MyNet(paddle.nn.Layer): def __init__(self): super(MyNet,self).__init__() self.layer=paddle.vision.models.resnet50(pretrained=True) self.fc = paddle.nn.Linear(1000, 12) #网络的前向计算过程 def forward(self,x): x=self.layer(x) x=self.fc(x) return x登录后复制
模型训练
1、定义模型输入规格,实例化自定义的MyNet模型,并使用PaddlePaddle的高级API paddle.Model 进行了封装;
2、配置Adam优化器和交叉熵损失函数,设置准确率作为评估指标,并在GPU上进行了模型的训练和评估;
3、设置日志打印频率和模型保存频率,指定模型保存目录;
通过观察训练过程,准确度高达97.3%!
step 77/77 - loss: 0.7499 - acc: 0.9440 - 759ms/stepEval begin...step 20/20 - loss: 3.6955e-06 - acc: 0.9737 - 712ms/stepEval samples: 1219Epoch 15/15登录后复制 In [ ]
# 定义输入input_define = paddle.static.InputSpec(shape=[-1,3,224,224], dtype="float32", name="img")label_define = paddle.static.InputSpec(shape=[-1,1], dtype="int64",name="label")# 实例化网络对象并定义优化器等训练逻辑model = MyNet()model = paddle.Model(model,inputs=input_define,labels=label_define) # 用Paddle.Model()对模型进行封装optimizer = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())# 上述优化器中的学习率(learning_rate)参数很重要。要是训练过程中得到的准确率呈震荡状态,忽大忽小,可以试试进一步把学习率调低。place = paddle.CUDAPlace(0) # 使用第一个GPU设备model.prepare(optimizer=optimizer, # 指定优化器 loss=paddle.nn.CrossEntropyLoss(), # 指定损失函数 metrics=paddle.metric.Accuracy()) # 指定评估方法model.fit(train_data=train_dataset, # 训练数据集 eval_data=eval_dataset, # 测试数据集 batch_size=64, # 一个批次的样本数量 epochs=15, # 迭代轮次 save_dir="/home/aistudio/model/", # 把模型参数、优化器参数保存至自定义的文件夹 save_freq=2, # 设定每隔多少个epoch保存模型参数及优化器参数 log_freq=100, # 打印日志的频率)登录后复制
推理测试
数据格式定义
1、定义一个用于推理的数据集类InferDataset,接受单个图像路径并进行预处理;
2、这个类继承自PaddlePaddle的Dataset类,提供了单张图像的读取和预处理功能,方便模型对单个图像进行预测;
In [79]class InferDataset(Dataset): def __init__(self, img_path=None): """ 数据读取Reader(推理) :param img_path: 推理单张图片 """ super().__init__() if img_path: self.img_paths = [img_path] else: raise Exception("请指定需要预测对应图片路径") def __getitem__(self, index): # 获取图像路径 img_path = self.img_paths[index] # 使用Pillow来读取图像数据并转成Numpy格式 img = Image.open(img_path) if img.mode != 'RGB': img = img.convert('RGB') img = preprocess(img) #数据预处理--这里仅包括简单数据预处理,没有用到数据增强 return img def __len__(self): return len(self.img_paths)登录后复制 实例化模型推理
1、实例化推理模型并加载了训练好的参数;
2、通过model.predict方法对指定图像进行预测,获取了预测结果,并根据结果索引输出了对应的类别名称;
In [90]import matplotlib.pyplot as pltfrom PIL import Image#实例化推理模型input_define = paddle.static.InputSpec(shape=[-1,3,224,224], dtype="float32", name="img")model = paddle.Model(MyNet(),inputs=input_define)#读取刚刚训练好的参数model.load('/home/aistudio/model/final')#准备模型model.prepare()#利用训练好的模型进行预测infer_path='/home/aistudio/work/images/wblx_sp/IMG_20240220_103938_15.JPG'infer_data = InferDataset(infer_path)result = model.predict(test_data=infer_data)[0] #关键代码,实现预测功能result = paddle.to_tensor(result)result = np.argmax(result.numpy()) #获得最大值所在的序号class_label_list=['ph_bj','ph_sp','ph_sz','ph_tg','sz_bj','sz_sp','sz_sz','sz_tg','wblx_bj','wblx_sp','wblx_sz','wblx_tg']label_description_dict = { 'ph_sp': '蒲黄-生品', 'ph_bj': '蒲黄-不及', 'ph_sz': '蒲黄-适中', 'ph_tg': '蒲黄-太过', 'sz_sp': '山楂-生品', 'sz_bj': '山楂-不及', 'sz_sz': '山楂-适中', 'sz_tg': '山楂-太过', 'wblx_sp': '王不留行-生品', 'wblx_bj': '王不留行-不及', 'wblx_sz': '王不留行-适中', 'wblx_tg': '王不留行-太过'}print('识别结果为:'+label_description_dict[class_label_list[result]])# 使用Pillow库读取图像img = Image.open(infer_path)# 显示图像plt.figure(figsize=(8, 6))plt.imshow(img)plt.axis('off') # 不显示坐标轴plt.show()登录后复制 Predict begin...step 1/1 [==============================] - 33ms/stepPredict samples: 1识别结果为:王不留行-生品登录后复制
登录后复制
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
逼AI当山顶洞人!Claude防话痨插件爆火,网友:受够了AI废话
新智元报道编辑:元宇【新智元导读】一个让AI像原始人一样说话的插件,在HN上一夜爆火,冲破2w星。它的核心只是一条简单粗暴的prompt:删掉冠词、客套和一切废话,号称能省下75%的输出token。
季度利润翻 8 倍,最赚钱的「卖铲人」财报背后,内存涨价狂潮如何收场?
AI 时代最赚钱的公司,可能从来不是做 AI 的那个。作者|张勇毅编辑|靖宇淘金热里最稳赚的人,从来不是淘金的,是卖铲子的。这句老话在 2026 年的科技行业又应验了一次。只不过这次卖铲子的不是英伟
Claude Code Harness+龙虾科研团来了!金字塔分层架构+多智能体
Claw AI Lab团队量子位 | 公众号 QbitAI你还在一个人做科研吗?科研最难的,从来不是问题本身,而是一个想法从文献到实验再到写作,只能靠自己一点点往前推。一个人方向偏了没人提醒,遇到歧
让离线强化学习从「局部描摹」变「全局布局」丨ICLR'26
面对复杂连续任务的长程规划,现有的生成式离线强化学习方法往往会暴露短板。它们生成的轨迹经常陷入局部合理但全局偏航的窘境。它们太关注眼前的每一步,却忘了最终的目的地。针对这一痛点,厦门大学和香港科技大
美国犹他州启动新试点项目:AI为患者开具精神类药物处方
IT之家 4 月 5 日消息,据外媒 PC Mag 当地时间 4 月 4 日报道,美国医疗机构 Legion Health 在犹他州获得监管批准,启动一项试点项目,允许 AI 系统为患者开具精神类药
- 日榜
- 周榜
- 月榜
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

