基于卷积神经网络的花卉识别分类

导入必要的包

import tensorflow as tf
import pandas as pd
import random
import pathlib
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

#导入模型包
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Flatten, Dropout, Conv2D, MaxPooling2D, BatchNormalization
from tensorflow.keras import layers, models

#导入优化器包
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.callbacks import ModelCheckpoint, TensorBoard  # 添加TensorBoard导入

#导入解压工具包
import zipfile
import os

1.加载数据

1.1解压数据集与查看数据

zip_file_path = '/data/bigfiles/flower_photos.zip'
extract_to_path = 'extracted_files'  #解压路径

os.makedirs(extract_to_path, exist_ok=True)    # 创建目标目录(如果不存在)

with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:         # 解压文件
    zip_ref.extractall(extract_to_path)

print(f"文件已解压到: {extract_to_path}")
文件已解压到: extracted_files
# 列出目录中的所有文件和子目录

extract_to_path = 'extracted_files/flower_photos/roses'
files = os.listdir(extract_to_path)

# 打印文件列表
print("解压后的文件列表:")
for file in files:
    print(file)
解压后的文件列表:
10090824183_d02c613f10_m.jpg
102501987_3cdb8e5394_n.jpg

......

1.2数据预处理

确定数据集的数据类型、数据尺寸、标签、是否划分训练集与测试集等

def dataset(data_path, image_size=(32,32)):
    data_path = pathlib.Path(data_path)
    all_image_paths = list(data_path.glob('*/*'))
    all_image_paths = [str(path) for path in all_image_paths]
    random.shuffle(all_image_paths)
    image_count = len(all_image_paths)
    print('数据量大小:', image_count )

    label_names = sorted(item.name for item in data_path.glob('*/') if item.is_dir())
    print('分类名:', label_names)

    label_to_index = dict((name, index) for index, name in enumerate(label_names))
    print('标签:', label_to_index)

    all_image_labels = np.array([label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths])

    all_image_data = []
    for path in all_image_paths:
        img = Image.open(path)
        img = img.resize(image_size)
        img = np.array(img)/255.0
        all_image_data.append(img)
    all_image_data = np.array(all_image_data)

    # 补充数据划分代码
    train_images, test_images, train_labels, test_labels = train_test_split(
        all_image_data, all_image_labels, test_size=0.2, random_state=42
    )
    
    return train_images, test_images, train_labels, test_labels, label_names

# 获取数据和标签信息
data_path = 'extracted_files/flower_photos'
train_images, test_images, train_labels, test_labels, label_names = dataset(data_path)
input_shape = (32,32,3)
classes = len(label_names)
class_dict = {i: name for i, name in enumerate(label_names)}
数据量大小: 1340
分类名: ['roses', 'sunflowers']
标签: {'roses': 0, 'sunflowers': 1}

2.构建模型

def myCNN(input_shape, classes):
    model = models.Sequential()
    model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3,3), activation='relu'))
    model.add(layers.MaxPooling2D((2,2)))
    model.add(layers.Conv2D(64, (3,3), activation='relu'))
    model.add(layers.Flatten())
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(classes, activation='softmax'))  # 修正输出层
    model.summary()
    return model

2.1构建LeNet模型

from tensorflow.keras import layers, Model

class LeNet(Model):  # 修正类名和继承自Model
    def __init__(self, classes=2):  # 添加classes参数
        super(LeNet, self).__init__()  # 修正super调用
        # 定义各层
        self.conv1 = layers.Conv2D(6, (5,5), activation='relu')  # 修正Conv20为Conv2D
        self.pool1 = layers.AveragePooling2D((2,2))
        self.conv2 = layers.Conv2D(16, (5,5), activation='relu')
        self.pool2 = layers.AveragePooling2D((2,2))
        self.flatten = layers.Flatten()  # 修正flatten为Flatten
        self.dense1 = layers.Dense(120, activation='relu')
        self.dense2 = layers.Dense(84, activation='relu')
        self.dense3 = layers.Dense(classes, activation='softmax')  # 使用参数设置输出单元

    def call(self, inputs):
        # 定义前向传播
        x = self.conv1(inputs)
        x = self.pool1(x)
        x = self.conv2(x)  # 修正inputs为x,保持数据流
        x = self.pool2(x)
        x = self.flatten(x)
        x = self.dense1(x)
        x = self.dense2(x)
        return self.dense3(x)  # 返回最后一层的输出

# 使用示例
input_shape = (32, 32, 3)  # 假设输入尺寸
classes = 5                # 分类数

# 创建模型实例
model = LeNet(classes=classes)

# 构建模型(需要指定输入形状)
model.build(input_shape=(None,) + input_shape)
model.summary()

# 编译模型
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 训练示例(需要实际数据)
# history = model.fit(train_images, train_labels, epochs=10, validation_data=(val_images, val_labels))
Model: "le_net"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_6 (Conv2D)            multiple                  456       
_________________________________________________________________
average_pooling2d (AveragePo multiple                  0         
_________________________________________________________________
conv2d_7 (Conv2D)            multiple                  2416      
_________________________________________________________________
average_pooling2d_1 (Average multiple                  0         
_________________________________________________________________
flatten_2 (Flatten)          multiple                  0         
_________________________________________________________________
dense_4 (Dense)              multiple                  48120     
_________________________________________________________________
dense_5 (Dense)              multiple                  10164     
_________________________________________________________________
dense_6 (Dense)              multiple                  425       
=================================================================
Total params: 61,581
Trainable params: 61,581
Non-trainable params: 0

2.2构建AlexNet模型

from tensorflow.keras import models, layers
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout

def AlexNet(input_shape, classes):
    """
    简化的AlexNet顺序模型
    参数:
        input_shape -- 输入图像的形状 (height, width, channels)
        classes -- 分类数量
    返回:
        Keras Sequential模型
    """
    model = models.Sequential()
    
    # 第一卷积层
    model.add(Conv2D(32, (3,3), strides=(1,1), activation='relu', input_shape=input_shape))
    model.add(MaxPooling2D((2,2), strides=(2,2)))
    
    # 第二卷积层
    model.add(Conv2D(64, (3,3), padding='same', activation='relu'))
    model.add(MaxPooling2D((2,2), strides=(2,2)))
    
    # 连续三个卷积层
    model.add(Conv2D(96, (3,3), padding='same', activation='relu'))
    model.add(Conv2D(96, (3,3), padding='same', activation='relu'))
    model.add(Conv2D(64, (3,3), padding='same', activation='relu'))
    model.add(MaxPooling2D((2,2), strides=(2,2)))
    
    # 展平层
    model.add(Flatten())
    
    # 全连接层(建议在大型数据集上取消Dropout注释)
    model.add(Dense(1024, activation='relu'))
    # model.add(Dropout(0.5))  # 防止过拟合
    model.add(Dense(512, activation='relu'))
    # model.add(Dropout(0.5))  # 防止过拟合
    
    # 输出层
    model.add(Dense(classes, activation='softmax'))
    
    model.summary()
    return model

# 使用示例
input_shape = (32, 32, 3)  # 输入图像尺寸
classes = 5                # 假设有5个花卉类别

# 初始化模型
model = AlexNet(input_shape, classes)

# 编译模型
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 训练模型 (需要传入训练数据)
# history = model.fit(train_images, train_labels, epochs=10, validation_data=(val_images, val_labels))
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_8 (Conv2D)            (None, 30, 30, 32)        896       
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 15, 15, 32)        0         
_________________________________________________________________
conv2d_9 (Conv2D)            (None, 15, 15, 64)        18496     
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 7, 7, 64)          0         
_________________________________________________________________
conv2d_10 (Conv2D)           (None, 7, 7, 96)          55392     
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 7, 7, 96)          83040     
_________________________________________________________________
conv2d_12 (Conv2D)           (None, 7, 7, 64)          55360     
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 3, 3, 64)          0         
_________________________________________________________________
flatten_3 (Flatten)          (None, 576)               0         
_________________________________________________________________
dense_7 (Dense)              (None, 1024)              590848    
_________________________________________________________________
dense_8 (Dense)              (None, 512)               524800    
_________________________________________________________________
dense_9 (Dense)              (None, 5)                 2565      
=================================================================
Total params: 1,331,397
Trainable params: 1,331,397
Non-trainable params: 0

2.3构建VGG模型

# 函数式模型VGG
def VGG(input_shape, classes):
    input = Input(shape=input_shape)
    
    # Block 1
    x = Conv2D(32, (3,3), strides=(1,1), padding='same', activation='relu', name='conv1')(input)
    x = Conv2D(32, (3,3), strides=(1,1), padding='same', activation='relu', name='conv2')(x)
    x = MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='valid', name='pool1')(x)
    
    # Block 2
    x = Conv2D(64, (3,3), strides=(1,1), padding='same', activation='relu', name='conv3')(x)
    x = Conv2D(64, (3,3), strides=(1,1), padding='same', activation='relu', name='conv4')(x)
    x = MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='valid', name='pool2')(x)
    
    # Block 3
    x = Conv2D(128, (3,3), strides=(1,1), padding='same', activation='relu', name='conv5')(x)
    x = Conv2D(128, (3,3), strides=(1,1), padding='same', activation='relu', name='conv6')(x)
    x = Conv2D(128, (3,3), strides=(1,1), padding='same', activation='relu', name='conv7')(x)
    x = MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='valid', name='pool3')(x)
    
    # Fully connected layers
    x = Flatten()(x)
    x = Dense(256, activation='relu', name='fc1')(x)
    x = Dropout(0.5)(x)
    x = Dense(256, activation='relu', name='fc2')(x)
    x = Dropout(0.5)(x)
    x = Dense(classes, activation='softmax', name='fc_predictions')(x)
    
    model = Model(inputs=input, outputs=x, name='VGG')
    model.summary()
    return model

# 在模型构建部分使用VGG模型
model = VGG(input_shape, classes)  # 替换原来的myCNN

# 编译模型
learning_rate = 0.001
sgd = SGD(learning_rate=learning_rate, momentum=0.9)
model.compile(loss='sparse_categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy'])

# 训练模型
num_epochs = 20
history = model.fit(
    train_images, train_labels,
    epochs=num_epochs,
    validation_data=(test_images, test_labels),
    callbacks=[checkpoint, tensorboard]
)
Model: "VGG"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 32, 32, 3)]       0         
_________________________________________________________________

......

import matplotlib.pyplot as plt

def plot_training_history(history):
    """
    绘制训练过程中的loss和accuracy曲线
    
    参数:
    history -- 由model.fit()返回的训练历史对象
    """
    # 提取训练和验证的loss和accuracy数据
    train_loss = history.history['loss']
    val_loss = history.history['val_loss']
    train_acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    
    # 创建画布
    plt.figure(figsize=(12, 5))
    
    # 绘制loss曲线
    plt.subplot(1, 2, 1)
    plt.plot(train_loss, label='Train Loss')
    plt.plot(val_loss, label='Validation Loss')
    plt.title('Training and Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    
    # 绘制accuracy曲线
    plt.subplot(1, 2, 2)
    plt.plot(train_acc, label='Train Accuracy')
    plt.plot(val_acc, label='Validation Accuracy')
    plt.title('Training and Validation Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()
    
    plt.tight_layout()
    plt.show()

# 在模型训练后调用绘图函数
plot_training_history(history)  # 使用训练返回的history对象

3.模型编译

#“请完善'?'处代码,选择模型”
model = myCNN(input_shape, classes)  # 选择模型

#“请完善'?'处代码,选择学习率”
learning_rate = 0.001

sgd = SGD(learning_rate=learning_rate, momentum=0.9)

#“请完善此处代码,设置模型编译,loss='sparse_categorical_crossentropy', optimizer=sgd, metrics=['accuracy']”
# 编译模型
model.compile(loss='sparse_categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy'])
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 30, 30, 32)        896       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 15, 15, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 13, 13, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 6, 6, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 4, 4, 64)          36928     
_________________________________________________________________
flatten (Flatten)            (None, 1024)              0         
_________________________________________________________________
dense (Dense)                (None, 64)                65600     
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 130       
=================================================================
Total params: 122,050
Trainable params: 122,050
Non-trainable params: 0

4.模型训练

num_epochs = 20

#“请完善此处代码,使用fit方法完成模型训练”
# 创建回调
checkpoint = ModelCheckpoint(
    'extracted_files/ckpt/cp-{epoch:04d}.ckpt',
    save_weights_only=True,
    verbose=1,
    save_freq='epoch'
)
tensorboard = TensorBoard(log_dir='extracted_files/logs')

# 训练模型
history = model.fit(
    train_images, train_labels,
    epochs=num_epochs,
    validation_data=(test_images, test_labels),
    callbacks=[checkpoint, tensorboard]
)
Epoch 1/20
34/34 [==============================] - 2s 54ms/step - loss: 0.6862 - accuracy: 0.5392 - val_loss: 0.6732 - val_accuracy: 0.6157

......

Epoch 00020: saving model to extracted_files/ckpt/cp-0020.ckpt

5.模型评估

#“请完善此处代码,使用evaluate方法完成模型评估”
test_loss, test_acc = model.evaluate(test_images, test_labels)  # 评估模型
print('准确率{0:.2f}%'.format(test_acc*100))
9/9 [==============================] - 0s 4ms/step - loss: 0.2619 - accuracy: 0.8731
准确率87.31%
#设置权重保存路径
ckpt_dir = 'extracted_files/ckpt'
if not os.path.exists(ckpt_dir):
    os.makedirs(ckpt_dir)

check_path = 'extracted_files/ckpt/cp-{epoch:04d}.ckpt'
checkpoint = ModelCheckpoint(check_path, save_weights_only=True, verbose=1, save_freq='epoch')

log_dir = os.path.join('extracted_files/logs')
if not os.path.exists(log_dir):
    os.mkdir(log_dir)

tensorboard = TensorBoard(log_dir = log_dir)

6.单张图片预测

data_path_1 = '/data/bigfiles/predict_1.jpg'
img_1 = Image.open(data_path_1)
plt.imshow(img_1)
plt.axis('off')
plt.show

data_path_2 = '/data/bigfiles/predict_2.jpg'
img_2 = Image.open(data_path_2)
plt.imshow(img_2)
plt.axis('off')
plt.show
<function matplotlib.pyplot.show(*args, **kw)>

class_dict = {0:'roses',1:'sunflowers'}
class Predict():
    def __init__(self):
        
        #“请完善'?'处代码,选择模型”
        self.network = myCNN(input_shape, classes)  # 初始化模型
        self.network.load_weights('extracted_files/ckpt/cp-0020.ckpt')
        self.class_dict = class_dict
    
    def predict(self, image_path):
        img = Image.open(image_path)
        img = img.resize((32,32), resample = Image.BILINEAR)
        x = np.expand_dims(np.array(img)/255.0, axis=0)
        
        #“请完善'?'处代码,模型预测”
        y = self.network.predict(x)  # 执行预测
        
        class_index = np.argmax(y[0])
        class_name = self.class_dict[class_index]
        print(y[0],'预测结果:',class_name)
        return class_name

if __name__ == '__main__':
    pre = Predict()
    pre.predict('/data/bigfiles/predict_1.jpg')
    pre.predict('/data/bigfiles/predict_2.jpg')
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_3 (Conv2D)            (None, 30, 30, 32)        896       
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 15, 15, 32)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 13, 13, 64)        18496     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 6, 6, 64)          0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 4, 4, 64)          36928     
_________________________________________________________________
flatten_1 (Flatten)          (None, 1024)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 64)                65600     
_________________________________________________________________
dense_3 (Dense)              (None, 2)                 130       
=================================================================
Total params: 122,050
Trainable params: 122,050
Non-trainable params: 0
_________________________________________________________________
[0.03079483 0.96920514] 预测结果: sunflowers
[0.9230295  0.07697055] 预测结果: roses
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值