### 深度可分离卷积检测算法 Language: Python 使用TensorFlow 深度学习框架,使用Keras会大幅缩减代码量 训练机器:华为Atlas 200 AI开发板(或本地计算机) [数据集](../../../medicine-dataset) 常用的**卷积网络模型**及在ImageNet上的准确率 |模型|大小|Top-1准确率|Top-5准确率|参数数量|深度| |:---:|:---:|:---:|:---:|:---:|:---:| |Xception|88 MB|0.790|0.945|22,910,480|126| |VGG16|528 MB|0.713|0.901|138,357,544|23| |VGG19|549 MB|0.713|0.900|143,667,240|26| |ResNet50|98 MB|0.749|0.921|25,636,712|168| |ResNet101|171 MB|0.764|0.928|44,707,176|-| |ResNet152|232 MB|0.766|0.931|60,419,944|-| |ResNet50V2|98 MB|0.760|0.930|25,613,800|-| |ResNet101V2|171 MB|0.772|0.938|44,675,560|-| |ResNet152V2|232 MB|0.780|0.942|60,380,648|-| |ResNeXt50|96 MB|0.777|0.938|25,097,128|-| |ResNeXt101|170 MB|0.787|0.943|44,315,560|-| |InceptionV3|92 MB|0.779|0.937|23,851,784|159| |InceptionResNetV2|215 MB|0.803|0.953|55,873,736|572| |MobileNet|16 MB|0.704|0.895|4,253,864|88| |MobileNetV2|14 MB|0.713|0.901|3,538,984|88| |DenseNet121|33 MB| 0.750|0.923|8,062,504|121| |DenseNet169|57 MB| 0.762|0.932|14,307,880|169| |DenseNet201|80 MB|0.773|0.936|20,242,984|201| |NASNetMobile|23 MB|0.744|0.919|5,326,716|-| |NASNetLarge|343 MB|0.825|0.960|88,949,818|-| 由于硬件条件限制,综合考虑模型的准确率、大小以及复杂度等因素,采用了**Xception模型**, 该模型是134层(包含激活层,批标准化层等)拓扑深度的卷积网络模型。 ## 检测算法 ```python def Xception(include_top=True, weights='imagenet', input_tensor=None, input_shape=None, pooling=None, classes=1000, **kwargs) # 参数 # include_top:是否保留顶层的全连接网络 # weights:None代表随机初始化,即不加载预训练权重。'imagenet’代表加载预训练权重 # input_tensor:可填入Keras tensor作为模型的图像输入tensor # input_shape:可选,仅当include_top=False有效,应为长为3的tuple,指明输入图片的shape,图片的宽高必须大于71,如(150,150,3) # pooling:当include_top=False时,该参数指定了池化方式。None代表不池化,最后一个卷积层的输出为4D张量。‘avg’代表全局平均池化,‘max’代表全局最大值池化。 # classes:可选,图片分类的类别数,仅当include_top=True并且不加载预训练权重时可用 ``` [基于Xception的模型微调,详细请参考代码](../../../medicine-model/src) 1. 设置Xception参数 迁移学习参数权重加载:xception_weights ```python # 设置输入图像的宽高以及通道数 img_size = (299, 299, 3) base_model = keras.applications.xception.Xception(include_top=False, weights='..\\resources\\keras-model\\xception_weights_tf_dim_ordering_tf_kernels_notop.h5', input_shape=img_size, pooling='avg') # 全连接层,使用softmax激活函数计算概率值,分类大小是628 model = keras.layers.Dense(628, activation='softmax', name='predictions')(base_model.output) model = keras.Model(base_model.input, model) # 锁定卷积层 for layer in base_model.layers: layer.trainable = False ``` 2. 全连接层训练(v1.0) ```python from base_model import model # 设置训练集图片大小以及目录参数 img_size = (299, 299) dataset_dir = '..\\dataset\\dataset' img_save_to_dir = 'resources\\image-traing\\' log_dir = 'resources\\train-log' model_dir = 'resources\\keras-model\\' # 使用数据增强 train_datagen = keras.preprocessing.image.ImageDataGenerator( rescale=1. / 255, shear_range=0.2, width_shift_range=0.4, height_shift_range=0.4, rotation_range=90, zoom_range=0.7, horizontal_flip=True, vertical_flip=True, preprocessing_function=keras.applications.xception.preprocess_input) test_datagen = keras.preprocessing.image.ImageDataGenerator( preprocessing_function=keras.applications.xception.preprocess_input) train_generator = train_datagen.flow_from_directory( dataset_dir, save_to_dir=img_save_to_dir, target_size=img_size, class_mode='categorical') validation_generator = test_datagen.flow_from_directory( dataset_dir, save_to_dir=img_save_to_dir, target_size=img_size, class_mode='categorical') # 早停法以及动态学习率设置 early_stop = EarlyStopping(monitor='val_loss', patience=13) reduce_lr = ReduceLROnPlateau(monitor='val_loss', patience=7, mode='auto', factor=0.2) tensorboard = keras.callbacks.tensorboard_v2.TensorBoard(log_dir=log_dir) for layer in model.layers: layer.trainable = False # 模型编译 model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']) history = model.fit_generator(train_generator, steps_per_epoch=train_generator.samples // train_generator.batch_size, epochs=100, validation_data=validation_generator, validation_steps=validation_generator.samples // validation_generator.batch_size, callbacks=[early_stop, reduce_lr, tensorboard]) # 模型导出 model.save(model_dir + 'chinese_medicine_model_v1.0.h5') ``` 3. 对于顶部的6层卷积层,我们使用数据集对权重参数进行微调 ```python # 加载模型 model=keras.models.load_model('resources\\keras-model\\chinese_medicine_model_v2.0.h5') for layer in model.layers: layer.trainable = False for layer in model.layers[126:132]: layer.trainable = True history = model.fit_generator(train_generator, steps_per_epoch=train_generator.samples // train_generator.batch_size, epochs=100, validation_data=validation_generator, validation_steps=validation_generator.samples // validation_generator.batch_size, callbacks=[early_stop, reduce_lr, tensorboard]) model.save(model_dir + 'chinese_medicine_model_v2.0.h5') ``` 4. 在后端项目中,我们使用Deeplearn4j调用训练好的模型 ``` public class CnnModelUtil { private static ComputationGraph CNN_MODEL = null; /** * 中药名字的编码 */ private static final Map MEDICINE_NAME_MAP = new HashMap<>(); /** * 定义cnn model的文件夹路径 */ private static final String DATA_DIR = System.getProperty("os.name") .toLowerCase().contains("windows") ? "D:\\data\\model\\" : "./data/model/"; /** * 定义中药编码表的文件名 */ private static final String MEDICINE_LABLE_FILE_NAME = "medicine_name-lable.txt"; /** * 定义模型的文件名 */ private static final String CNN_MODEL_FILE_NAME = "chinese_medicine_model.h5"; /** * 图片的加载器 */ private static final NativeImageLoader IMAGE_LOADER = new NativeImageLoader(299, 299, 3); /** * 初始化 */ static { try { CNN_MODEL = KerasModelImport.importKerasModelAndWeights(DATA_DIR + CNN_MODEL_FILE_NAME); Files.readAllLines(Paths.get(DATA_DIR, MEDICINE_LABLE_FILE_NAME)).forEach(v -> { String[] split = v.split(","); MEDICINE_NAME_MAP.put(Integer.valueOf(split[1]), split[0]); }); } catch (IOException | InvalidKerasConfigurationException | UnsupportedKerasConfigurationException e) { e.printStackTrace(); } } /** * 对图像进行预测 * 对预测的概率值进行排序处理 * 返回值是概率值前10的中药的名字 * @param file * @return * @throws */ public static Map medicineNamePredict(File file) throws IOException { INDArray image = IMAGE_LOADER.asMatrix(file).divi(127.5).subi(1); INDArray output = CNN_MODEL.outputSingle(image); Map resultMap = new HashMap<>(); float[] floats = output.toFloatVector(); for (int i = 0; i < floats.length; i++) { resultMap.put(i, floats[i]); } List> resultList = new LinkedList<>(resultMap.entrySet()); resultList.sort(Map.Entry.comparingByValue(Comparator.reverseOrder())); Map medicinePredict = new LinkedHashMap<>(); resultList.stream().limit(10).forEach(v -> { medicinePredict.put(MEDICINE_NAME_MAP.get(v.getKey()), v.getValue()); }); return medicinePredict; } } ``` ### 模型概览 [模型详细结构](../../assets/images/model.png) **训练过程正确率以及损失函数可视化展示** ![正确率](../../assets/images/正确率.png) ![损失函数](../../assets/images/损失函数.png)