Python手写数字识别

news2025/1/20 0:50:48

神经网络是手写数字识别中常用的机器学习模型。它由许多神经元组成,每个神经元接收输入并生成输出。在前向传递过程中,神经元计算一些权重和偏移量的线性组合,并将其输入到一个非线性的激活函数中,从而生成神经元的输出。输出层通常使用softmax函数,将神经网络的输出映射到每个数字类别的概率。训练神经网络通常使用反向传播算法,该算法用于计算网络中每个权重和偏移量的梯度,并用梯度下降算法调整这些参数以最小化损失函数。在训练完成后,手写数字图像可以通过前向传递神经网络来进行分类。梯度下降法是寻找损失函数最小值的常用方法,包括批量梯度下降、随机梯度下降和小批量梯度下降。选择哪种梯度下降法取决于具体的算法和任务需求

本项目的源代码已放在文章末尾,如果它对您有帮助,请点个赞或收藏支持一下哦~。

神经网络结构

一个完整的神经网络通常由多个基本的网络层堆叠而成。本项目中的三层全连接神经网络由三个全连接层构成,在每两个全连接层之间插入 ReLU 激活函数以引入非线性变换,最后使用 Softmax 层计算交叉熵损失,如图所示。因此本项目中使用的基本单元包括全连接层、ReLU 激活函数、Softmax 损失函数
在这里插入图片描述

MINIST数据集

MNIST数据集是一个手写数字图像数据集,由60,000个训练图像和10,000个测试图像组成。每个图像都是28x28像素的灰度图像,表示0到9之间的一个数字。该数据集常用于机器学习中的图像分类任务。

获取MNIST数据集的步骤如下:

  1. 访问MNIST官方网站:http://yann.lecun.com/exdb/mnist/
  2. 点击“Download”链接,下载四个压缩文件:train-images-idx3-ubyte.gz、train-labels-idx1-ubyte.gz、t10k-images-idx3-ubyte.gz和t10k-labels-idx1-ubyte.gz。
  3. 解压缩这四个文件,得到四个二进制文件。
  4. 使用Python等编程语言读取这四个文件,将其转换为图像和标签数据。可以使用第三方库,如numpy和scikit-learn,来读取和处理数据。

代码解读

mian.py

# coding=utf-8
import numpy as np
import struct
import os
import time
import matplotlib.pyplot as plt

from layers_1 import FullyConnectedLayer, ReLULayer, SoftmaxLossLayer

# 画图时的中文支持
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 定义了 MNIST 数据集的存储路径和文件名
MNIST_DIR = "./mnist_data"
TRAIN_DATA = "train-images-idx3-ubyte"
TRAIN_LABEL = "train-labels-idx1-ubyte"
TEST_DATA = "t10k-images-idx3-ubyte"
TEST_LABEL = "t10k-labels-idx1-ubyte"


# 显示矩阵的形状和均值和标准差
def show_matrix(mat, name):
    # print(name + str(mat.shape) + ' mean %f, std %f' % (mat.mean(), mat.std()))
    pass


# 该类包括多个函数,用于加载数据集、建立模型、初始化模型、训练和评估
# 定义一个类名为 MNIST_MLP,表示一个多层感知机(Multilayer Perceptron)模型
class MNIST_MLP(object):
    # 初始化函数,传入了多个参数用于初始化神经网络
    def __init__(self, batch_size=90, input_size=784, hidden1=32, hidden2=16, out_classes=10, lr=0.01, max_epoch=2,
                 print_iter=100):
        self.batch_size = batch_size  # 每次训练迭代所使用的数据批次大小
        self.input_size = input_size  # 输入层神经元数量(即MNIST图像大小)
        self.hidden1 = hidden1  # 第一层隐藏层神经元数量
        self.hidden2 = hidden2  # 第二层隐藏层神经元数量
        self.out_classes = out_classes  # 输出层神经元数量(即分类数量)
        self.lr = lr  # 学习率,用于控制模型训练时每次参数的更新幅度
        self.max_epoch = max_epoch  # 最大训练轮数
        self.print_iter = print_iter  # 训练过程中每隔print_iter次输出一次日志信息
        self.accuracy_list = []  # 存储每次迭代的准确率
        self.loss_list = []  # 存储每次迭代损失

    # 该函数用于读取 MNIST 数据集的图像和标记,并将其存储为 numpy 数组,其中 is_images 参数为 True 表示读取图像,为 False 表示读取标记
    # 定义一个函数来读取mnist数据集
    def load_mnist(self, file_dir, is_images='True'):
        # 打开二进制文件并读取其中的数据
        bin_file = open(file_dir, 'rb')
        bin_data = bin_file.read()
        bin_file.close()
        # 解析文件头
        if is_images:
            # 如果是图像数据集,读取图像格式的文件头
            fmt_header = '>iiii'
            magic, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, 0)
        else:
            # 如果是标签数据集,读取标签格式的文件头
            fmt_header = '>ii'
            magic, num_images = struct.unpack_from(fmt_header, bin_data, 0)
            num_rows, num_cols = 1, 1
        # 计算数据的大小
        data_size = num_images * num_rows * num_cols
        # 解析数据
        mat_data = struct.unpack_from('>' + str(data_size) + 'B', bin_data, struct.calcsize(fmt_header))
        # 将数据重塑为一个矩阵
        mat_data = np.reshape(mat_data, [num_images, num_rows * num_cols])
        # 打印读取数据的相关信息
        print('Load images from %s, number: %d, data shape: %s' % (file_dir, num_images, str(mat_data.shape)))
        # 返回读取到的数据
        return mat_data

    # 调用 load_mnist 函数读取和存储 MNIST 中训练数据和测试数据的图像和标记
    def load_data(self):
        # 调用函数 load_mnist 读取和预处理 MNIST 中训练数据和测试数据的图像和标记
        # print('Loading MNIST data from files...')
        print('加载 MNIST 数据集 ...')
        train_images = self.load_mnist(os.path.join(MNIST_DIR, TRAIN_DATA), True)
        train_labels = self.load_mnist(os.path.join(MNIST_DIR, TRAIN_LABEL), False)
        test_images = self.load_mnist(os.path.join(MNIST_DIR, TEST_DATA), True)
        test_labels = self.load_mnist(os.path.join(MNIST_DIR, TEST_LABEL), False)
        self.train_data = np.append(train_images, train_labels, axis=1)
        self.test_data = np.append(test_images, test_labels, axis=1)
        # print(train_images.shape)
        # print(train_labels.shape)
        # print(test_images.shape)
        # print(test_labels.shape)

    # 将训练数据集打乱,以便更好地训练
    def shuffle_data(self):
        print('随机混洗 MNIST 数据...')
        np.random.shuffle(self.train_data)

    # 建立多层感知机神经网络,并定义各层的数量及其之间的连接关系
    def build_model(self):  # 建立网络结构
        print('构建多层感知机模型...')
        self.fc1 = FullyConnectedLayer(self.input_size, self.hidden1)
        self.relu1 = ReLULayer()
        self.fc2 = FullyConnectedLayer(self.hidden1, self.hidden2)
        self.relu2 = ReLULayer()
        self.fc3 = FullyConnectedLayer(self.hidden2, self.out_classes)
        self.softmax = SoftmaxLossLayer()
        self.update_layer_list = [self.fc1, self.fc2, self.fc3]

    # 初始化神经网络参数
    def init_model(self):
        # print('Initializing parameters of each layer in MLP...')
        print('初始化各层参数权值...')

        for layer in self.update_layer_list:
            layer.init_param()

    # 从文件加载神经网络参数(即权重和偏差)
    def load_model(self, param_dir):  # 加载神经网络权值
        print('从文件加载参数权值:' + param_dir)
        params = np.load(param_dir, allow_pickle=True).item()
        self.fc1.load_param(params['w1'], params['b1'])
        self.fc2.load_param(params['w2'], params['b2'])
        self.fc3.load_param(params['w3'], params['b3'])

    # 将神经网络的参数(即权重和偏差)保存到文件中以备后续调用
    def save_model(self, param_dir):
        print('保存权值和偏置到: ' + param_dir)
        params = {}
        params['w1'], params['b1'] = self.fc1.save_param()
        params['w2'], params['b2'] = self.fc2.save_param()
        params['w3'], params['b3'] = self.fc3.save_param()
        np.save(param_dir, params)

    # 神经网络的前向传播,即将输入数据通过神经网络传递,得出预测结果
    def forward(self, input):  # 神经网络的前向传播
        h1 = self.fc1.forward(input)
        h1 = self.relu1.forward(h1)
        h2 = self.fc2.forward(h1)
        h2 = self.relu2.forward(h2)
        h3 = self.fc3.forward(h2)
        prob = self.softmax.forward(h3)
        return prob

    # 神经网络的反向传播,即求解梯度,调整神经网络参数
    def backward(self):  # 神经网络的反向传播
        # TODO:神经网络的反向传播
        dloss = self.softmax.backward()
        dh3 = self.fc3.backward(dloss)
        dh2 = self.relu2.backward(dh3)
        dh2 = self.fc2.backward(dh2)
        dh1 = self.relu1.backward(dh2)
        dh1 = self.fc1.backward(dh1)

    # 根据梯度和学习率更新神经网络参数
    def update(self, lr):  # 神经网络的参数更新
        for layer in self.update_layer_list:
            layer.update_param(lr)

    # 使用反向传播和梯度下降训练多层感知机神经网络,使其逐渐适应数据集
    def train(self):
        # 计算每个batch中的样本数
        max_batch = self.train_data.shape[0] // self.batch_size  # [0]表示数据集的第一维,也就是数据集中样本的数量
        print('开始训练...')
        # 对于每个epoch
        for idx_epoch in range(3):
            # 将训练数据集随机打乱
            self.shuffle_data()
            # 对于每个batch
            for idx_batch in range(max_batch):
                # 从训练数据中取出当前batch的图片和标签
                batch_images = self.train_data[idx_batch * self.batch_size:(idx_batch + 1) * self.batch_size, :-1]
                batch_labels = self.train_data[idx_batch * self.batch_size:(idx_batch + 1) * self.batch_size, -1]
                # 使用前向传播算法计算预测概率
                prob = self.forward(batch_images)
                # 使用Softmax交叉熵函数计算损失
                loss = self.softmax.get_loss(batch_labels)
                # 使用反向传播算法进行梯度下降更新模型参数
                self.backward()
                self.update(self.lr)
                # 如果当前batch的序号可以整除打印迭代步数的间隔,打印该batch的损失
                if idx_batch % self.print_iter == 0:
                    print('Epoch %d, iter %d, loss: %.6f' % (idx_epoch, idx_batch, loss))
                    #  计算当前batch的预测准确率并存储下来
                    pred_results = np.zeros([batch_labels.shape[0]])
                    for idx in range(batch_labels.shape[0] // self.batch_size):
                        # 从当前batch中取出样本进行预测
                        batch_images_temp = batch_images[idx * self.batch_size:(idx + 1) * self.batch_size, :]
                        prob = self.forward(batch_images_temp)
                        pred_labels = np.argmax(prob, axis=1)
                        pred_results[idx * self.batch_size:(idx + 1) * self.batch_size] = pred_labels
                    # 计算准确率
                    acc = np.mean(pred_results == batch_labels)
                    self.accuracy_list.append(acc)  # 存储当前batch的预测准确率
                    self.loss_list.append(loss)  # 存储当前batch的预测准确率

    # 在测试数据集上对神经网络进行推断,并计算分类准确率
    def evaluate(self):  # 推断函数
        pred_results = np.zeros([self.test_data.shape[0]])
        start_time = time.time()
        for idx in range(self.test_data.shape[0] // self.batch_size):
            batch_images = self.test_data[idx * self.batch_size:(idx + 1) * self.batch_size, :-1]
            prob = self.forward(batch_images)
            end = time.time()
            # print(prob.shape)
            pred_labels = np.argmax(prob, axis=1)
            pred_results[idx * self.batch_size:(idx + 1) * self.batch_size] = pred_labels
        print("测试总时间: %f" % (time.time() - start_time))
        accuracy = np.mean(pred_results == self.test_data[:, -1])
        print('测试集准确率: %f' % accuracy)

        plt.plot(range(len(self.accuracy_list)), self.accuracy_list)
        plt.xlabel('迭代次数')
        plt.ylabel('准确率')
        plt.title('训练过程中准确率变化')
        plt.show()

        plt.plot(range(len(self.loss_list)), self.loss_list)
        plt.xlabel('迭代次数')
        plt.ylabel('准确率')
        plt.title('训练过程中损失变化')
        plt.show()


# 创建 MNIST_MLP 类的实例,并对参数进行初始化、加载数据、建立模型、初始化模型、训练、保存模型、加载模型和评估的一系列操作
if __name__ == '__main__':
    h1, h2, e = 32, 16, 1
    mlp = MNIST_MLP(hidden1=h1, hidden2=h2, max_epoch=e)
    mlp.load_data()
    mlp.build_model()
    mlp.init_model()
    start_time = time.time()
    mlp.train()
    print("训练总时间: %f" % (time.time() - start_time))
    mlp.save_model('mlp-%d-%d-%depoch.npy' % (h1, h2, e))
    mlp.load_model('mlp-%d-%d-%depoch.npy' % (h1, h2, e))
    mlp.evaluate()

这是一个使用多层感知机(MLP)进行手写数字分类的Python代码。

  1. 定义常量:定义了 MNIST 数据集的存储路径和文件名。
  2. 函数 load_mnist:该函数用于读取 MNIST 数据集的图像和标记,并将其存储为 numpy 数组,其中 is_images 参数为 True 表示读取图像,为 False 表示读取标记。
  3. 函数 show_matrix:显示矩阵的形状和均值和标准差。
  4. 类 MNIST_MLP:该类包括多个函数,用于加载数据集、建立模型、初始化模型、训练和评估。
  5. 函数 load_data:调用 load_mnist 函数读取和存储 MNIST 中训练数据和测试数据的图像和标记。
  6. 函数 shuffle_data:将训练数据集打乱,以便更好地训练。
  7. 函数 build_model:建立多层感知机神经网络,并定义各层的数量及其之间的连接关系。
  8. 函数 init_model:初始化神经网络参数。
  9. 函数 load_model:从文件加载神经网络参数(即权重和偏差)。
  10. 函数 save_model:将神经网络的参数(即权重和偏差)保存到文件中以备后续调用。
  11. 函数 forward:神经网络的前向传播,即将输入数据通过神经网络传递,得出预测结果。
  12. 函数 backward:神经网络的反向传播,即求解梯度,调整神经网络参数。
  13. 函数 update:根据梯度和学习率更新神经网络参数。
  14. 函数 train:使用反向传播和梯度下降训练多层感知机神经网络,使其逐渐适应数据集。
  15. 函数 evaluate:在测试数据集上对神经网络进行推断,并计算分类准确率。
  16. main 函数中,创建 MNIST_MLP 类的实例,并对参数进行初始化、加载数据、建立模型、初始化模型、训练、保存模型、加载模型和评估的一系列操作。
# coding=utf-8    # 代码文件编码方式

import numpy as np   # 引入numpy库
import struct        # 引入struct库
import os            # 引入os库
import time          # 引入time库

# 实现了一个全连接层类,num_input是输入维度,num_output是输出维度
class FullyConnectedLayer(object):
    def __init__(self, num_input, num_output):
        self.num_input = num_input
        self.num_output = num_output
        print('\tFully connected layer with input %d, output %d.' % (self.num_input, self.num_output))

    # 初始化参数,std是初始化参数的标准差
    def init_param(self, std=0.01):
        self.weight = np.random.normal(loc=0.0, scale=std, size=(self.num_input, self.num_output))
        self.bias = np.zeros([1, self.num_output])

    # 前向传播计算, input是输入
    def forward(self, input):
        start_time = time.time()
        self.input = input
        # 全连接层的前向传播,计算输出结果
        self.output = np.dot(self.input, self.weight) + self.bias
        return self.output

    # 反向传播计算,top_diff是损失函数对输出的导数
    def backward(self, top_diff):
        # 计算参数梯度和本层损失
        self.d_weight = np.dot(self.input.T, top_diff)
        self.d_bias = np.sum(top_diff, axis=0, keepdims=True)
        bottom_diff = np.dot(top_diff, self.weight.T)
        return bottom_diff

    # 利用参数进行参数更新,lr是学习率
    def update_param(self, lr):
        self.weight -= lr * self.d_weight
        self.bias -= lr * self.d_bias

    # 加载参数,用于重新加载模型
    def load_param(self, weight, bias):
        assert self.weight.shape == weight.shape
        assert self.bias.shape == bias.shape
        self.weight = weight
        self.bias = bias

    # 用于保存参数,方便模型重新训练
    def save_param(self):
        return self.weight, self.bias

# 实现了一个ReLU激活函数层类
class ReLULayer(object):
    def __init__(self):
        print('\tReLU layer.')

    # 计算前向传播的输出结果
    def forward(self, input):
        start_time = time.time()
        self.input = input
        # ReLU层的前向传播,计算输出结果
        output = np.maximum(0, input)
        return output

    # 计算反向传播输出的损失结果
    def backward(self, top_diff):
        # ReLU层的反向传播,计算本层损失
        bottom_diff = top_diff.copy()
        bottom_diff[self.input<0] = 0
        return bottom_diff

# 实现了一个softmax激活函数层类
class SoftmaxLossLayer(object):
    def __init__(self):
        print('\tSoftmax loss layer.')

    # 计算前向传播的输出结果
    def forward(self, input):
        # softmax 损失层的前向传播,计算输出结果
        input_max = np.max(input, axis=1, keepdims=True)
        input_exp = np.exp(input - input_max)
        self.prob = input_exp / np.sum(input_exp, axis=1, keepdims=True)
        return self.prob

    # 计算损失
    def get_loss(self, label):
        self.batch_size = self.prob.shape[0]
        self.label_onehot = np.zeros_like(self.prob)
        self.label_onehot[np.arange(self.batch_size), label] = 1.0
        loss = -np.sum(np.log(self.prob) * self.label_onehot) / self.batch_size
        return loss

    # 计算反向传播输出的损失结果
    def backward(self):
        # softmax 损失层的反向传播,计算本层损失
        bottom_diff = (self.prob - self.label_onehot) / self.batch_size
        return bottom_diff


这段代码实现了一个简单的神经网络,包括全连接层、ReLU激活函数层和softmax损失函数层。其中,全连接层用于将输入数据进行线性变换,ReLU激活函数层用于增加网络的非线性能力,softmax损失函数层用于计算分类问题中的损失函数。代码中实现了前向传播和反向传播的计算,并且提供了参数初始化、参数更新、参数加载和保存等功能,方便模型的重新训练。

项目源码: https://gitee.com/TGY1817/handwritten-digit-recognition

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/625708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6道常见hadoop面试题及答案解析

Q1.什么是Hadoop&#xff1f;   Hadoop是一个开源软件框架&#xff0c;用于存储大量数据&#xff0c;并发处理/查询在具有多个商用硬件&#xff08;即低成本硬件&#xff09;节点的集群上的那些数据。总之&#xff0c;Hadoop包括以下内容&#xff1a;   HDFS&#xff08;Ha…

Scrum经验性过程

软件开发是一个复杂的活动&#xff0c; 在软件产品开发的过程中不仅存在着需求的不确定性&#xff0c;也存在着技术的不确定性&#xff0c;再加上参与软件开发的主体通常是由多人组成的软件开发团队&#xff0c;加上人的因素&#xff0c;就让整个软件开发的活动变得非常复杂。如…

MySQL的索引详解

1.什么是MySQL的索引 1.1索引的概念 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c; 并指定索引的类型&#xff0c;各类索引有各自的数据结构实现。 所以索引归根结底只是在做一件事&#xff0c;添加了索…

数据结构-Redis(三)

前面介绍了redis的String和哈希&#xff0c;接下来看看其他的数据结构 List LPUSH&#xff1a;左边放入 RPUSH&#xff1a;右边放入 LPOP&#xff1a;取出左边第一个数&#xff0c;并且移除 RPOP&#xff1a;取出右边第一个数&#xff0c;并且移除 由上操作可以看出&#…

chatgpt赋能python:Python中一行输出的方法

Python中一行输出的方法 Python是一种高级编程语言&#xff0c;其语法简洁、易于阅读、丰富的库和解释器使其成为了众多程序员的选择。在Python中有时需要一行输出多个值、变量或者其他信息&#xff0c;因此在本文中将介绍如何在Python中实现一行输出的方法。 一般的输出方法…

chatgpt赋能python:Python如何一行一行运行?

Python 如何一行一行运行&#xff1f; Python是一门广泛应用于开发Web、科学计算、人工智能等领域的高级编程语言。相比其他编程语言&#xff0c;Python简单易学&#xff0c;语法简洁优雅&#xff0c;拥有许多强大的第三方库和工具。但作为一个新手&#xff0c;可能会对Python…

【复习笔记】FreeRTOS(四) 列表项的插入和删除

本文是FreeRTOS复习笔记的第四节&#xff0c;列表项的插入和删除。 上一篇文章&#xff1a; 【复习笔记】FreeRTOS(三)任务挂起和恢复 文章目录 一、列表和列表项1.1. 列表1.2. 列表项1.3. 迷你列表项 二、实验目的三、测试例程四、实验效果 一、列表和列表项 列表和列表项是F…

Dubbo 注册,调用,通信,容错

Dubbo简化模型 3种开发方式 开发方式 举例 特点 XML配置 等 业务代码零侵入 扩展修改方便 注解方式 EnableDubbo DubboService DubboReference 扩展修改方便 修改需要重新编译代码 API编程 DubboBootstrap ServiceConfig ReferenceConfig应用 业务侵入性大 修改复杂…

【前端学习】React学习资料

React 是一种开源的 JavaScript 库&#xff0c;用于构建用户界面。它由 Facebook 开发并维护&#xff0c;已经成为了当今最流行的前端库之一。与其他框架不同&#xff0c;React 主要专注于视图层&#xff08;View&#xff09;&#xff0c;旨在通过声明式、组件化的方式来构建复…

Pagination分页(antd-design组件库)展示所有配置选项和onChange的作用

1.Pagination分页 采用分页的形式分隔长列表&#xff0c;每次只加载一个页面。 2.何时使用 当加载/渲染所有数据将花费很多时间时&#xff1b; 可切换页码浏览数据。 组件代码来自&#xff1a; 分页 Pagination - Ant Design 3.本地验证前的准备 参考文章【react项目antd组件-d…

redis到底是怎么样进行渐进式hash的

Redis 是一个开源&#xff08;BSD许可&#xff09;的&#xff0c;内存中的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中间件。那么redis的底层是如何来存储数据的呢&#xff1f; 一、redis如何在存储大量的key时候&#xff0c;查询速度还能接近O(1)呢&#xf…

分布式事务方案学习

第100篇文章啦&#xff01;分布式事务在面试中分布式事务也是十分重要的点&#xff0c;所以学习完分布式锁后我们就来学习分布式事务吧。 事务表示的是我们在业务逻辑中对数据库进行操作的一组单元&#xff0c;需要同时成功或同时失败&#xff0c;不了解的小伙伴们可以看一下下…

Linux编译器-gcc/g++(动静态链接)

目录 一、Linux编译器-gcc/g的使用1.1 背景知识1.2 预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接1.3 动静态链接 二、补充sudo设置 一、Linux编译器-gcc/g的使用 1.1 背景知识 我们为什么能在windows或者linux下进行C/C或者其它形式的开发呢&#xff1f;前提条件…

我在VScode学Java(Java一维数组、二维数组、JVM中的堆和栈)重制版

​ 我的个人博客主页&#xff1a;如果’真能转义1️⃣说1️⃣的博客主页 关于Java基本语法学习---->可以参考我的这篇博客&#xff1a;《我在VScode学Java》 Java一维数组、二维数组 零._.在Java中_什么是数组Java 数组是一种数据结构&#xff0c;存储一组相同类型的数据。引…

Docker网络模型(七)使用 IPvlan 网络

使用 IPvlan 网络 IPvlan 驱动为用户提供了全面控制 IPv4 和 IPv6 寻址的能力。 IPvlan 让操作者能完全操控二层&#xff08;数据链路层&#xff09;网络的 vlan 标签&#xff0c;甚至也提供了三层&#xff08;网络传输层&#xff09;路由控制给感兴趣的用户。对于抽象出物理限…

【SpringSecurity】CSRF、环境配置、授权、认证功能、记住我功能实现

SpringSecurity 文章目录 SpringSecurityCSRF跨站请求伪造攻击开发环境搭建认证直接认证使用数据库认证自定义登录界面 授权基于角色的授权基于权限的授权使用注解判断权限 记住我SecurityContext SpringSecurity是一个基于Spring开发的非常强大的权限验证框架&#xff0c;其核…

Java快速安装以及入门指南

安装 Java 环境教程 Java 是一种广泛应用于软件开发、Web 应用程序和移动应用程序等领域的编程语言。如果您要使用 Java 进行开发或运行需要 Java 程序&#xff0c;您需要先在计算机上安装 Java 环境。 本教程将向您介绍如何在 Windows 操作系统上安装和验证 Java 环境。还将…

第2章 可行性研究

文章目录 第2章 可行性研究2.1 可行性研究的任务2.2 可行性研究过程2.3 系统流程图2.3.1符号2.3.3 分层 2.4 数据流图2.4.1 符号1. 数据源点或终点2. 数据加工&#xff08;变换数据的处理&#xff09;3. 数据存储4.数据流数据流与数据加工之间的关系 2.4.2绘制数据流图的例子顶…

chatgpt赋能python:Python3.6.5到Python3.7.5:升级指南

Python 3.6.5到Python 3.7.5&#xff1a;升级指南 Python是一种广泛使用的编程语言&#xff0c;拥有强大的库和框架&#xff0c;能够开发各种类型的应用程序。在Python的发行版中&#xff0c;版本更新是常见的过程&#xff0c;以提供更好的性能和新的功能。 本文将介绍如何将…

手记系列之六 ----- 分享个人使用kafka经验

前言 本篇文章主要介绍的关于本人从刚工作到现在使用kafka的经验&#xff0c;内容非常多&#xff0c;包含了kafka的常用命令&#xff0c;在生产环境中遇到的一些场景处理&#xff0c;kafka的一些web工具推荐等等。由于kafka这块的记录以及经验是从我刚开始使用kafka&#xff0…