训练集链接如下,路径需要自行调整

https://download.csdn.net/download/Sr6220033/90164947?spm=1001.2014.3001.5503

1. 训练模型

import os
import numpy as np
from PIL import Image
from sklearn.model_selection import train_test_split
import tensorflow as tf

# 定义数据集路径
train_path = r"C:\Users\HP\Desktop\data\training"
test_path = r"C:\Users\HP\Desktop\data\testing"

# 目标图片尺寸(例如28x28)
img_size = (28, 28)


# 载入数据和标签的函数
def load_data(dataset_path, img_size):
    images = []
    labels = []

    # 遍历每个数字的文件夹
    for label in os.listdir(dataset_path):
        label_path = os.path.join(dataset_path, label)

        if os.path.isdir(label_path):  # 如果是文件夹(每个文件夹对应一个数字标签)
            for img_name in os.listdir(label_path):
                img_path = os.path.join(label_path, img_name)

                # 读取图片并调整大小
                try:
                    img = Image.open(img_path).convert('L')  # 转换为灰度图
                    img = img.resize(img_size)  # 调整为目标大小
                    img_array = np.array(img)  # 转换为numpy数组
                    images.append(img_array)
                    labels.append(int(label))  # 标签是文件夹名,即数字
                except Exception as e:
                    print(f"Error loading image {img_path}: {e}")

    # 转换为numpy数组
    images = np.array(images)
    labels = np.array(labels)

    # 标准化处理:将像素值归一化到0-1之间
    images = images / 255.0

    # 扩展维度:由于图片是二维的(高x宽),我们需要为每张图片添加一个通道维度
    images = images.reshape(-1, img_size[0], img_size[1], 1)  # (样本数, 高度, 宽度, 通道数)

    return images, labels


# 加载训练集和验证集数据
X_train, y_train = load_data(train_path, img_size)
X_test, y_test = load_data(test_path, img_size)

# 划分训练集和验证集(80%训练,20%验证)
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42)

# 将标签转换为one-hot编码
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
y_val = tf.keras.utils.to_categorical(y_val, num_classes=10)
y_test = tf.keras.utils.to_categorical(y_test, num_classes=10)

# 输出数据形状
print(f"训练集图片形状: {X_train.shape}")
print(f"验证集图片形状: {X_val.shape}")
print(f"测试集图片形状: {X_test.shape}")
print(f"训练集标签形状: {y_train.shape}")
print(f"验证集标签形状: {y_val.shape}")
print(f"测试集标签形状: {y_test.shape}")

# 构建更复杂的神经网络模型(使用卷积神经网络)
model = tf.keras.Sequential([
    # 卷积层1
    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D((2, 2)),
    # 卷积层2
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),
    # 卷积层3
    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),

    # Flatten层:将二维的卷积输出展平为一维
    tf.keras.layers.Flatten(),

    # 全连接层
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dropout(0.5),
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.Dropout(0.5),

    # 输出层
    tf.keras.layers.Dense(10, activation='softmax')  # 使用softmax激活函数
])

# 编译模型
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
              loss='categorical_crossentropy',  # 使用交叉熵作为误差函数
              metrics=['accuracy'])

# 训练模型
model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_val, y_val))

# 测试模型
test_loss, test_accuracy = model.evaluate(X_test, y_test)
print(f"测试集损失: {test_loss}, 测试集准确率: {test_accuracy}")

# 保存训练好的模型为 .h5 文件
model.save("handwriting_model.h5")

验证模型并创建手写版

import tkinter as tk
import numpy as np
from PIL import Image, ImageDraw
import tensorflow as tf

# 加载已经训练好的模型
model = tf.keras.models.load_model("handwriting_model.h5")  # 加载之前训练的模型

# 目标图片尺寸(28x28)
img_size = (28, 28)

# 创建一个Tkinter窗口
root = tk.Tk()
root.title("实时手写数字识别")

# 创建画布,白色背景,黑色笔
canvas = tk.Canvas(root, width=280, height=280, bg="white")
canvas.grid(row=0, column=0)

# 存储绘制的手写图像
image = Image.new("L", (280, 280), color=0)  # 创建一个黑色背景的空图像
draw = ImageDraw.Draw(image)


# 鼠标事件处理函数,用来绘制
def draw_line(event):
    x1, y1 = (event.x - 2), (event.y - 2)
    x2, y2 = (event.x + 2), (event.y + 2)
    canvas.create_oval(x1, y1, x2, y2, fill="black", width=10)  # 绘制黑色圆点
    draw.line([x1, y1, x2, y2], fill=255, width=10)  # 在图像上绘制白色线条


canvas.bind("<B1-Motion>", draw_line)


# 清除画布的函数
def clear_canvas():
    canvas.delete("all")
    global image, draw
    image = Image.new("L", (280, 280), color=0)  # 重新创建黑色背景的图像
    draw = ImageDraw.Draw(image)


# 预测图像的函数
def predict():
    # 将绘制的图像缩放为 28x28 并转换为 numpy 数组
    img_resized = image.resize(img_size, Image.Resampling.LANCZOS)
    img_array = np.array(img_resized) / 255.0  # 归一化
    img_array = img_array.reshape(-1, 28, 28, 1)  # 适配模型输入形状

    # 预测
    prediction = model.predict(img_array)
    predicted_digit = np.argmax(prediction)  # 获取概率最高的标签

    # 显示预测结果
    result_label.config(text=f"预测结果: {predicted_digit}")


# 创建按钮并显示预测结果
predict_button = tk.Button(root, text="预测", command=predict)
predict_button.grid(row=1, column=0)

# 创建清除按钮
clear_button = tk.Button(root, text="清除", command=clear_canvas)
clear_button.grid(row=2, column=0)

# 创建结果显示标签
result_label = tk.Label(root, text="预测结果: ", font=("Arial", 20))
result_label.grid(row=3, column=0)

# 运行Tkinter事件循环
root.mainloop()

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐