Kaggle-Plant Seedlings Classification-(多分类+CNN+图形处理)
Plant Seedlings Classification
题意:
就是给出很多图片,预测出图片中的小草是什么类型的。
数据处理:
1.去除图片背景,只保留物体。
2.图片数据加强。
建立模型:
1.CNN的Sequential(),多层卷积块和全连接层。
2.学习率降低回调函数。
3.查看混淆矩阵进一步考虑优化。
代码:
import cv2
from glob import glob
import numpy as np
from matplotlib import pyplot as plt
import math
import pandas as pd# 设置图像缩放尺寸和随机种子
ScaleTo = 70 # 图像缩放尺寸
seed = 7 # 随机种子,保证结果可复现print("读取训练图片" + "/"*70 + "\n")
path = '/kaggle/input/plant-seedlings-classification/train/*/*.png' # 训练集路径
files = glob(path) # 获取所有图像文件路径trainImg = [] # 存储训练图像
trainLabel = [] # 存储训练标签
j = 1
num = len(files)# 读取图像并调整大小,同时获取标签
for img in files:print(str(j) + "/" + str(num), end="\r")trainImg.append(cv2.resize(cv2.imread(img), (ScaleTo, ScaleTo))) # 读取图像并调整大小trainLabel.append(img.split('/')[-2]) # 从路径中提取标签(文件夹名)j += 1trainImg = np.asarray(trainImg) # 转换为numpy数组
trainLabel = pd.DataFrame(trainLabel) # 转换为DataFrame# 显示前8张训练图像
for i in range(8):plt.subplot(2, 4, i + 1)plt.imshow(trainImg[i])print("去掉图片背景,只保留物体本身" + "/"*70 + "\n")
clearTrainImg = [] # 存储去背景后的图像
examples = []
getEx = True# 对每张图像进行背景去除处理
for img in trainImg:# 使用高斯模糊减少噪声blurImg = cv2.GaussianBlur(img, (5, 5), 0)# 转换为HSV颜色空间,便于颜色分割hsvImg = cv2.cvtColor(blurImg, cv2.COLOR_BGR2HSV)# 创建掩码,提取绿色植物区域lower_green = (25, 40, 50) # 绿色下界upper_green = (75, 255, 255) # 绿色上界mask = cv2.inRange(hsvImg, lower_green, upper_green) # 创建二值掩码kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11)) # 定义结构元素mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 闭运算填充空洞# 创建布尔掩码bMask = mask > 0# 应用掩码,提取植物区域clear = np.zeros_like(img, np.uint8) # 创建空白图像clear[bMask] = img[bMask] # 仅保留掩码区域的像素clearTrainImg.append(clear) # 保存去背景后的图像# 显示处理过程示例if getEx:plt.subplot(2, 3, 1); plt.imshow(img) # 原始图像plt.subplot(2, 3, 2); plt.imshow(blurImg) # 模糊图像plt.subplot(2, 3, 3); plt.imshow(hsvImg) # HSV图像plt.subplot(2, 3, 4); plt.imshow(mask) # 掩码plt.subplot(2, 3, 5); plt.imshow(bMask) # 布尔掩码plt.subplot(2, 3, 6); plt.imshow(clear) # 去背景图像getEx = FalseclearTrainImg = np.asarray(clearTrainImg) # 转换为numpy数组# 显示前8张去背景后的图像
for i in range(8):plt.subplot(2, 4, i + 1)plt.imshow(clearTrainImg[i])clearTrainImg = clearTrainImg / 255 # 归一化处理print("处理训练数据集的label" + "/"*70 + "\n")
from tensorflow.keras.utils import to_categorical
from sklearn import preprocessing
import matplotlib.pyplot as plt# 标签编码
le = preprocessing.LabelEncoder()
le.fit(trainLabel[0])
print("类别: " + str(le.classes_)) # 显示所有类别
encodeTrainLabels = le.transform(trainLabel[0]) # 将标签转换为数字# 转换为one-hot编码
clearTrainLabel = to_categorical(encodeTrainLabels)
num_clases = clearTrainLabel.shape[1]
print("类别数量: " + str(num_clases))# 可视化各类别样本数量分布
trainLabel[0].value_counts().plot(kind='bar')print("分离训练和验证集" + "/"*70 + "\n")
from sklearn.model_selection import train_test_split# 划分训练集和验证集,保持类别平衡
trainX, testX, trainY, testY = train_test_split(clearTrainImg, clearTrainLabel,test_size=0.1, random_state=seed,stratify = clearTrainLabel)print("数据集加强" + "/"*70 + "\n")
from tensorflow.keras.preprocessing.image import ImageDataGenerator# 创建数据增强器,增加训练样本多样性
datagen = ImageDataGenerator(rotation_range=180, # 随机旋转角度范围zoom_range = 0.1, # 随机缩放范围width_shift_range=0.1, # 水平平移范围height_shift_range=0.1, # 垂直平移范围horizontal_flip=True, # 随机水平翻转vertical_flip=True # 随机垂直翻转)
datagen.fit(trainX) # 应用数据增强器print("构造神经网络CNN model" + "/"*70 + "\n")
import numpy
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalizationnumpy.random.seed(seed) # 固定随机种子# 构建CNN模型
model = Sequential()# 第一层卷积块
model.add(Conv2D(filters=64, kernel_size=(5, 5), input_shape=(ScaleTo, ScaleTo, 3), activation='relu'))
model.add(BatchNormalization(axis=3)) # 批量归一化
model.add(Conv2D(filters=64, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2))) # 最大池化
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1)) # Dropout正则化# 第二层卷积块
model.add(Conv2D(filters=128, kernel_size=(5, 5), activation='relu'))
model.add(BatchNormalization(axis=3))
model.add(Conv2D(filters=128, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))# 第三层卷积块
model.add(Conv2D(filters=256, kernel_size=(5, 5), activation='relu'))
model.add(BatchNormalization(axis=3))
model.add(Conv2D(filters=256, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))# 全连接层
model.add(Flatten()) # 展平多维数据
model.add(Dense(256, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # 较高的Dropout率防止过拟合model.add(Dense(256, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))model.add(Dense(num_clases, activation='softmax')) # 输出层,使用softmax激活函数model.summary() # 显示模型结构# 编译模型
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])print("训练model" + "/"*70 + "\n")from keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, CSVLogger# 学习率降低回调函数
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',patience=3, # 等待3个epoch没有改善verbose=1, # 显示更新信息factor=0.4, # 学习率降低因子min_lr=0.00001) # 最小学习率callbacks_list = [learning_rate_reduction] # 回调函数列表# 训练模型
hist = model.fit(datagen.flow(trainX, trainY, batch_size=75),epochs=35, validation_data=(testX, testY),steps_per_epoch=trainX.shape[0], callbacks=callbacks_list)print("混淆矩阵" + "/"*70 + "\n")
from sklearn.metrics import confusion_matrix
import itertools# 绘制混淆矩阵函数
def plot_confusion_matrix(cm, classes,normalize=False,title='Confusion matrix',cmap=plt.cm.Blues):fig = plt.figure(figsize=(10, 10))plt.imshow(cm, interpolation='nearest', cmap=cmap)plt.title(title)plt.colorbar()tick_marks = np.arange(len(classes))plt.xticks(tick_marks, classes, rotation=90)plt.yticks(tick_marks, classes)if normalize:cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]thresh = cm.max() / 2.for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):plt.text(j, i, cm[i, j],horizontalalignment="center",color="white" if cm[i, j] > thresh else "black")plt.tight_layout()plt.ylabel('真实标签')plt.xlabel('预测标签')# 预测验证集
predY = model.predict(testX)
predYClasses = np.argmax(predY, axis=1)
trueY = np.argmax(testY, axis=1)# 计算混淆矩阵
confusionMTX = confusion_matrix(trueY, predYClasses)# 绘制混淆矩阵
plot_confusion_matrix(confusionMTX, classes=le.classes_)print("处理测试集" + "/"*70 + "\n")
path = '/kaggle/input/plant-seedlings-classification/test/*.png' # 测试集路径
files = glob(path)testImg = [] # 存储测试图像
testId = [] # 存储测试图像ID
j = 1
num = len(files)# 读取测试图像
for img in files:print("读取图像: " + str(j) + "/" + str(num), end='\r')testId.append(img.split('/')[-1]) # 提取图像IDtestImg.append(cv2.resize(cv2.imread(img), (ScaleTo, ScaleTo))) # 调整图像大小j += 1testImg = np.asarray(testImg) # 转换为numpy数组# 显示前8张测试图像
for i in range(8):plt.subplot(2, 4, i + 1)plt.imshow(testImg[i])# 对测试图像进行背景去除处理(与训练集相同)
clearTestImg = []
examples = []
getEx = Truefor img in testImg:# 使用高斯模糊blurImg = cv2.GaussianBlur(img, (5, 5), 0)# 转换为HSV颜色空间hsvImg = cv2.cvtColor(blurImg, cv2.COLOR_BGR2HSV)# 创建掩码(绿色植物区域)lower_green = (25, 40, 50)upper_green = (75, 255, 255)mask = cv2.inRange(hsvImg, lower_green, upper_green)kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)# 创建布尔掩码bMask = mask > 0# 应用掩码,提取植物区域clear = np.zeros_like(img, np.uint8)clear[bMask] = img[bMask]clearTestImg.append(clear) # 保存去背景后的图像# 显示处理示例if getEx:plt.subplot(2, 3, 1); plt.imshow(img) # 原始图像plt.subplot(2, 3, 2); plt.imshow(blurImg) # 模糊图像plt.subplot(2, 3, 3); plt.imshow(hsvImg) # HSV图像plt.subplot(2, 3, 4); plt.imshow(mask) # 掩码plt.subplot(2, 3, 5); plt.imshow(bMask) # 布尔掩码plt.subplot(2, 3, 6); plt.imshow(clear) # 去背景图像getEx = FalseclearTestImg = np.asarray(clearTestImg) # 转换为numpy数组# 显示前8张去背景后的测试图像
for i in range(8):plt.subplot(2, 4, i + 1)plt.imshow(clearTestImg[i])clearTestImg = clearTestImg / 255 # 归一化处理# 预测测试集
pred = model.predict(clearTestImg)# 处理预测结果
predNum = np.argmax(pred, axis=1) # 获取最大概率的类别索引
predStr = le.classes_[predNum] # 将索引转换为类别名称# 保存结果到CSV文件
res = {'file': testId, 'species': predStr}
res = pd.DataFrame(res)
res.to_csv("/kaggle/working/res.csv", index=False)