实验四 Python聚类决策树训练与预测 && 基于神经网络的MNIST手写体识别

发布于:2025-03-14 ⋅ 阅读:(11) ⋅ 点赞:(0)

一、实验目的

Python聚类决策树训练与预测:

1、掌握决策树的基本原理并理解监督学习的基本思想。

2、掌握Python实现决策树的方法。

基于神经网络的MNIST手写体识别:

1、学习导入和使用Tensorflow。

2、理解学习神经网络的基本原理。

3、学习使用Python实现MNIST手写体识别。

二、实验原理

        运用Jupyter notebook平台编写实例Python决策树构建、训练、可视化及预测程序。运用Jupyter notebook平台编写实例Python神经网络程序。

三、使用软件平台

1、Windows 11电脑一台。

2、Anaconda、Python、Spyder平台Jupyter notebook平台。

四、实验内容

实例一:

实例1:基于ONE-HOT编码的银行贷款决策树 

基于sklearn库和ONE-HOT编码的决策树,并实现可视化。

要求:

(1)使用pandas读取银行贷款信息数据集。

(2)使用DictVectorizer做数据处理,实现输入特征向量化。

(3)利用sklearn库构建决策树并开展训练。

(4)利用pydotplus库完成决策树可视化。

结果:

代码:

# 导入必要库
import pandas as pd
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.feature_extraction import DictVectorizer
import matplotlib.pyplot as plt
import matplotlib as mpl
# 自动检测文件编码
import chardet
# 检测文件编码
file_path = 'loan_YN.csv'
with open(file_path, 'rb') as f:
    raw_data = f.read()
    detected_encoding = chardet.detect(raw_data)['encoding']

# 解决中文显示问题
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 使用中文字体(如 SimHei)
mpl.rcParams['axes.unicode_minus'] = False   # 正常显示负号

# 加载数据
data = pd.read_csv('loan_YN.csv', encoding=detected_encoding)  # 自动检测到的编码

# 查看数据前几行
print(data.head())

# 特征和目标变量
X = data[['age', 'income', 'guding', 'VIP']]
y = data['loan']

# 特征向量化(使用 DictVectorizer 进行 ONE-HOT 编码)
vec = DictVectorizer(sparse=False)
X_encoded = vec.fit_transform(X.to_dict(orient='records'))

# 训练决策树
clf = DecisionTreeClassifier(criterion='entropy', max_depth=4, random_state=42)
clf.fit(X_encoded, y)

# 可视化决策树
plt.figure(figsize=(20, 10))
plot_tree(clf, 
          feature_names=vec.get_feature_names_out(), 
          class_names=clf.classes_, 
          filled=True, 
          rounded=True)
plt.title("基于CSV数据的决策树")
plt.show()

思考题:

思考1:基于特征数字化的银行贷款决策树

要求:

(1)利用pandas导入特征数字化的银行贷款数据集,查看并分析数据内容。

(2)参照实例1完成决策树的构建和训练。

(3)利用pydotplus库完成决策树可视化。

(4)观察特征数字化和特征向量化对决策树的影响。

结果:

代码:

# 导入必要库
import pandas as pd
from sklearn.tree import DecisionTreeClassifier, export_graphviz
import pydotplus

# 加载数据
file_path = 'loan_YN_01.csv'  # 确保路径正确,例如 'C:/path_to_file/loan_YN_01.csv'
data = pd.read_csv(file_path)

# 特征和目标变量分离
X = data[['age', 'income', 'guding', 'VIP']]  # 特征
y = data['loan']  # 目标

# 构建并训练决策树
clf = DecisionTreeClassifier(criterion='entropy', max_depth=5, random_state=42)
clf.fit(X, y)

# 导出决策树为 DOT 格式
dot_data = export_graphviz(
    clf,
    out_file=None,
    feature_names=['age', 'income', 'guding', 'VIP'],  # 特征名
    class_names=['N', 'Y'],  # 类别名
    filled=True,
    rounded=True,
    special_characters=True
)

# 使用 pydotplus 生成图片
graph = pydotplus.graph_from_dot_data(dot_data)

# 保存图片
graph.write_png("decision_tree.png")
print("决策树图片已保存为 decision_tree.png")

实例2:构建、训练、保存神经网络模型,并输出训练及测试准确率。

用Python编程实现,导入TensorFlow包,定义神经网络模型,读取并且输入MNIST数据集的训练图片和标签数据,启动网络训练,输出得到网络不同批次训练的损失值和准确率。

要求:

(1)初始化参数矩阵,定义一个init_weights(shape)函数,使得该函数可以返回一个与输入形状相同的tensor,标准差为0.01,并且利用该函数生成shape=[784, 16]的全连接层1、shape=[16, 16]的全连接层2和shape=[16, 10]的输出层。

(2)初始化网络模型,定义一个按顺序与以上两个全连接层和输出层相乘的神经网络model(x,h1,h2,out),x代表输入训练数据,h1,h2,out分别代表(1)中定义的两个全连接层和输出层,并且返回最终的输出结果。

(3)定义好损失函数、优化器后,设置模型训练的训练次数和每批次送入模型的图片数量,开始模型训练,要求训练次数在代码开始运行后可以从键盘输入。

(4)每隔1000个训练批次输出一次平均损失值和训练准确率,损失值保留6位小数,准确率保留3位小数,最后输出一个平均测试准确率,保留三位小数。

结果:

代码:

import tensorflow as tf
import numpy as np
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tqdm import tqdm  # 导入 tqdm 用于进度条

# 导入数据集
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0
x_test = x_test.reshape(-1, 784).astype('float32') / 255.0
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

# 注册自定义类为可序列化对象
@tf.keras.utils.register_keras_serializable()
# 定义模型
class NeuralNetwork(tf.keras.Model):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.dense1 = tf.keras.layers.Dense(16, activation='relu')  # 第一隐藏层
        self.dense2 = tf.keras.layers.Dense(16, activation='relu')  # 第二隐藏层
        self.output_layer = tf.keras.layers.Dense(10)  # 输出层
    def call(self, x):
        x = self.dense1(x)
        x = self.dense2(x)
        return self.output_layer(x)
    def get_config(self):
       """返回模型配置字典"""
       config = super(NeuralNetwork, self).get_config()
       return config
    @classmethod
    def from_config(cls, config):
       """从配置字典恢复模型实例"""
       return cls()

# 自定义回调函数用于输出格式调整和进度条
class ProgressBarCallback(tf.keras.callbacks.Callback):
    def __init__(self, epochs):
        self.epochs = epochs
        self.progress_bar = tqdm(total=epochs, desc="训练进度", unit="epoch")

    def on_epoch_end(self, epoch, logs=None):
        # 更新进度条
        self.progress_bar.update(1)
        # 每 1000 次或最后一次打印日志
        if (epoch + 1) % 1000 == 0 or epoch + 1 == self.epochs:
            print(f"\n第 {epoch + 1} 次训练\t损失值: {logs['loss']:.6f}\t训练准确率: {logs['accuracy']:.3%}")

    def on_train_end(self, logs=None):
        # 关闭进度条
        self.progress_bar.close()

def calculate_average_accuracy(model, x, y, batch_size=128):
    num_batches = len(x) // batch_size
    accuracies = []
    for i in range(num_batches):
        batch_x = x[i * batch_size:(i + 1) * batch_size]
        batch_y = y[i * batch_size:(i + 1) * batch_size]
        _, acc = model.evaluate(batch_x, batch_y, verbose=0)
        accuracies.append(acc)
    return np.mean(accuracies)

# 初始化模型
model = NeuralNetwork()

# 编译模型
model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
    loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
    metrics=['accuracy']
)

# 输入训练次数
epochs = int(input("请输入训练次数: "))

# 训练模型,添加自定义回调和进度条
model.fit(
    x_train, y_train,
    batch_size=128,
    epochs=epochs,
    validation_data=(x_test, y_test),
    callbacks=[ProgressBarCallback(epochs)],
    verbose=0  # 禁用默认输出
)


# 测试集评估
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
# 计算测试集的平均准确率
average_test_acc = calculate_average_accuracy(model, x_test, y_test)

# 打印结果
print(f"测试集整体准确率: {test_acc:.3%}")
print(f"测试集平均准确率: {average_test_acc:.3%}")

# 使用 .keras 扩展名保存为 Keras 推荐的保存格式
model.save("neural_network_plus_model.keras")
print("模型已保存为 neural_network_model.keras")

实例3:可视化模型输出结果和标签。

导入matplotlib.pyplot和numpy,利用实验1中训练好的模型来预测MNIST测试集的图片,并且在得到结果的同时与数据集中的标签进行对比,判断预测是否正确。

要求:

(1)定义图片展示函数display_compare(num),可以通过键盘选择需要预测的图片,num为需要进行预测和判断的图片序号(范围为0-10000)。

(2)将输入图片重构为网络需要的维度([1,784])送入模型进行预测并且保留下来,通过argmax激活函数获得模型的输出数字和实际标签进行比较,判断预测是否正确。

(3)利用matplotlib.pyplot包,展示所预测原始图片(需要将输入重构为28*28大小才能恢复成原始图片样式),并且在图片title处输出预测值和标签和预测是否正确。

结果:

代码:

import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
from tensorflow.keras.models import load_model  # 用于加载模型

# 设置中文字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei']  # 设置字体为黑体
matplotlib.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 加载 .keras 格式
model = load_model("neural_network_model.keras")
print("模型已加载: neural_network_model.keras")
# 加载测试数据
(_, _), (x_test, y_test) = tf.keras.datasets.mnist.load_data()  # 仅加载测试集
x_test = x_test.reshape(-1, 784).astype('float32') / 255.0  # 数据归一化
y_test = tf.keras.utils.to_categorical(y_test, 10)  # One-hot 编码

def display_compare(num):
    """
    可视化预测和标签
    :param num: 测试集中的图片序号
    """
    # 提取图片和标签
    img = x_test[num].reshape(1, 784)
    label = y_test[num]

    # 模型预测
    logits = model(img)
    pred = tf.argmax(logits, axis=1).numpy()[0]
    true_label = tf.argmax(label).numpy()

    # 判断预测是否正确
    is_correct = (pred == true_label)

    # 绘制图片
    plt.imshow(img.reshape(28, 28), cmap='gray')
    plt.title(f"预测值: {pred}, 实际值: {true_label}, 预测{'正确' if is_correct else '错误'}")
    plt.axis('off')
    plt.show()

# 输入图片编号
num = int(input("请输入想要查看的图片编号(0-9999范围): "))
display_compare(num)

注意:必须现有训练模型,才能进行图片判断。