第六章.卷积神经网络(CNN)—CNN的实现(搭建手写数字识别的CNN)

news2024/11/24 3:46:36

第六章.卷积神经网络(CNN)

6.2 CNN的实现(搭建手写数字识别的CNN)

1.网络构成

在这里插入图片描述

2.代码实现

import pickle
import matplotlib.pyplot as plt
import numpy as np
import sys, os

sys.path.append(os.pardir)

from dataset.mnist import load_mnist
from collections import OrderedDict


# 从图像到矩阵
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    N, C, H, W = input_data.shape
    out_h = (H + 2 * pad - filter_h) // stride + 1
    out_w = (W + 2 * pad - filter_w) // stride + 1

    img = np.pad(input_data, [(0, 0), (0, 0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N * out_h * out_w, -1)

    return col


# 从矩阵到图像
def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    N, C, H, W = input_shape
    out_h = (H + 2 * pad - filter_h) // stride + 1
    out_w = (W + 2 * pad - filter_w) // stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)

    img = np.zeros((N, C, H + 2 * pad + stride - 1, W + 2 * pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]


class SGD:

    def __init__(self, lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]


class Momentum:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
            params[key] += self.v[key]


class Nesterov:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] *= self.momentum
            self.v[key] -= self.lr * grads[key]
            params[key] += self.momentum * self.momentum * self.v[key]
            params[key] -= (1 + self.momentum) * self.lr * grads[key]


class AdaGrad:

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)


class RMSprop:

    def __init__(self, lr=0.01, decay_rate=0.99):
        self.lr = lr
        self.decay_rate = decay_rate
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)


class Adam:

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None

    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)

        self.iter += 1
        lr_t = self.lr * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter)

        for key in params.keys():
            # self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
            # self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key] ** 2 - self.v[key])

            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

            # unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
            # unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
            # params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)


# 激活函数Relu
class Relu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0
        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout
        return dx


# 卷积层
class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad

        # 中间数据(backward时使用)
        self.x = None
        self.col = None
        self.col_W = None

        # 权重和偏置参数的梯度
        self.dW = None
        self.db = None

    # 正向传播
    def forward(self, x):
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        out_h = int((H + 2 * self.pad - FH) / self.stride) + 1
        out_w = int((W + 2 * self.pad - FW) / self.stride) + 1

        col = im2col(x, FH, FW, self.stride, self.pad)
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    # 反向传播
    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx


# 池化层
class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None
        self.arg_max = None

    # 正向传播
    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h * self.pool_w)

        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    # 反向传播
    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)

        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,))

        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)

        return dx


# Affine层
class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b

        self.x = None
        self.original_x_shape = None
        # 权重和偏置参数的导数
        self.dW = None
        self.db = None

    def forward(self, x):
        # 对应张量
        self.original_x_shape = x.shape  # 例如:x.shape=(209, 64, 64, 3)
        x = x.reshape(x.shape[0], -1)  # x=(209, 64*64*3)
        self.x = x

        out = np.dot(self.x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)

        dx = dx.reshape(*self.original_x_shape)  # 还原输入数据的形状(对应张量)
        return dx


# 输出层
class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None  # 损失
        self.y = None  # softmax的输出
        self.t = None  # 监督数据(one_hot vector)

    # 输出层函数:softmax
    def softmax(self, x):
        if x.ndim == 2:
            x = x.T
            x = x - np.max(x, axis=0)
            y = np.exp(x) / np.sum(np.exp(x), axis=0)
            return y.T

        x = x - np.max(x)  # 溢出对策
        return np.exp(x) / np.sum(np.exp(x))

    # 交叉熵误差
    def cross_entropy_error(self, y, t):
        if y.ndim == 1:
            t = t.reshape(1, t.size)
            y = y.reshape(1, y.size)

        # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
        if t.size == y.size:
            t = t.argmax(axis=1)

        batch_size = y.shape[0]
        return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

    # 正向传播
    def forward(self, x, t):
        self.t = t
        self.y = self.softmax(x)
        self.loss = self.cross_entropy_error(self.y, self.t)
        return self.loss

    # 反向传播
    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        if self.t.size == self.y.size:  # 监督数据是one-hot-vector的情况
            dx = (self.y - self.t) / batch_size
        else:
            dx = self.y.copy()
            dx[np.arange(batch_size), self.t] -= 1
            dx = dx / batch_size

        return dx


class Trainer:
    """进行神经网络的训练的类
    """

    def __init__(self, network, x_train, t_train, x_test, t_test,
                 epochs=20, mini_batch_size=100,
                 optimizer='SGD', optimizer_param={'lr': 0.01},
                 evaluate_sample_num_per_epoch=None, verbose=True):
        self.network = network
        self.verbose = verbose
        self.x_train = x_train
        self.t_train = t_train
        self.x_test = x_test
        self.t_test = t_test
        self.epochs = epochs
        self.batch_size = mini_batch_size
        self.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch

        # optimzer
        optimizer_class_dict = {'sgd': SGD, 'momentum': Momentum, 'nesterov': Nesterov,
                                'adagrad': AdaGrad, 'rmsprpo': RMSprop, 'adam': Adam}
        self.optimizer = optimizer_class_dict[optimizer.lower()](**optimizer_param)

        self.train_size = x_train.shape[0]
        self.iter_per_epoch = max(self.train_size / mini_batch_size, 1)
        self.max_iter = int(epochs * self.iter_per_epoch)
        self.current_iter = 0
        self.current_epoch = 0

        self.train_loss_list = []
        self.train_acc_list = []
        self.test_acc_list = []

    def train_step(self):
        batch_mask = np.random.choice(self.train_size, self.batch_size)
        x_batch = self.x_train[batch_mask]
        t_batch = self.t_train[batch_mask]

        grads = self.network.gradient(x_batch, t_batch)
        self.optimizer.update(self.network.params, grads)

        loss = self.network.loss(x_batch, t_batch)
        self.train_loss_list.append(loss)
        if self.verbose: print("train loss:" + str(loss))

        if self.current_iter % self.iter_per_epoch == 0:
            self.current_epoch += 1

            x_train_sample, t_train_sample = self.x_train, self.t_train
            x_test_sample, t_test_sample = self.x_test, self.t_test
            if not self.evaluate_sample_num_per_epoch is None:
                t = self.evaluate_sample_num_per_epoch
                x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]
                x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]

            train_acc = self.network.accuracy(x_train_sample, t_train_sample)
            test_acc = self.network.accuracy(x_test_sample, t_test_sample)
            self.train_acc_list.append(train_acc)
            self.test_acc_list.append(test_acc)

            if self.verbose: print(
                "=== epoch:" + str(self.current_epoch) + ", train acc:" + str(train_acc) + ", test acc:" + str(
                    test_acc) + " ===")
        self.current_iter += 1

    def train(self):
        for i in range(self.max_iter):
            self.train_step()

        test_acc = self.network.accuracy(self.x_test, self.t_test)

        if self.verbose:
            print("=============== Final Test Accuracy ===============")
            print("test acc:" + str(test_acc))


# 手写数字识别CNN的实现类: conv - relu - pool - affine - relu - affine - softmax
class SimpleConvNet:
    def __init__(self, input_dim=(1, 28, 28), conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
                 hidden_size=100, output_size=10, weight_int_std=0.01):
        filter_num = conv_param['filter_num']
        filter_size = conv_param['filter_size']
        filter_pad = conv_param['pad']
        filter_stride = conv_param['stride']

        input_size = input_dim[1]
        conv_output_size = (input_size + 2 * filter_pad - filter_size) / filter_stride + 1
        pool_output_size = int(filter_num * (conv_output_size / 2) * (conv_output_size / 2))

        # 初始化权重
        self.params = {}
        self.params['W1'] = weight_int_std * np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        self.params['W2'] = weight_int_std * np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        self.params['W3'] = weight_int_std * np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)

        # 生成层
        self.layers = OrderedDict()
        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'], filter_stride, filter_pad)
        self.layers['Relu1'] = Relu()
        self.layers['pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])
        self.last_layer = SoftmaxWithLoss()

    # 推理函数
    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)

        return x

    # 损失函数
    def loss(self, x, t):
        y = self.predict(x)
        return self.last_layer.forward(y, t)

    # 识别精度
    def accuracy(self, x, t, batch_size=100):
        if t.ndim != 1: t = np.argmax(t, axis=1)

        acc = 0.0
        for i in range(int(x.shape[0] / batch_size)):
            tx = x[i * batch_size:(i + 1) * batch_size]
            tt = t[i * batch_size:(i + 1) * batch_size]
            y = self.predict(tx)
            y = np.argmax(y, axis=1)
            acc += np.sum(y == tt)

        return acc / x.shape[0]

    def numerical_gradient(f, x):
        h = 1e-4  # 0.0001
        grad = np.zeros_like(x)

        it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
        while not it.finished:
            idx = it.multi_index
            tmp_val = x[idx]
            x[idx] = float(tmp_val) + h
            fxh1 = f(x)  # f(x+h)

            x[idx] = tmp_val - h
            fxh2 = f(x)  # f(x-h)
            grad[idx] = (fxh1 - fxh2) / (2 * h)

            x[idx] = tmp_val  # 还原值
            it.iternext()

        return grad

    # 数值微分
    def numerical_gradient(self, x, t):
        loss_w = lambda w: self.loss(x, t)
        grads = {}
        for idx in (1, 2, 3):
            grads['W' + str(idx)] = self.numerical_gradient(loss_w, self.params['W' + str(idx)])
            grads['b' + str(idx)] = self.numerical_gradient(loss_w, self.params['b' + str(idx)])

        return grads

    # 误差反向传播法求梯度
    def gradient(self, x, t):
        self.loss(x, t)
        dout = 1
        dout = self.last_layer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 设定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].db
        grads['W2'], grads['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W3'], grads['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads

    # 保存参数
    def save_param(self, file_name='params.pkl'):
        params = {}
        for key, val in self.params.items():
            params[key] = val

        with open(file_name, 'wb') as f:
            pickle.dump(params, f)

    # 加载参数
    def load_param(self, file_name='params.pkl'):
        with open(file_name, 'rb') as f:
            params = pickle.load(f)
        for key, val in params.items():
            self.params[key] = val

        for i, key in enumerate(['Conv1', 'Affine1', 'Affine2']):
            self.layers[key].W = self.params['W' + str(i + 1)]
            self.layers[key].b = self.params['b' + str(i + 1)]

#加载数据
(x_train,t_train),(x_test,t_test)=load_mnist(flatten=False)

#较少数据
x_train,t_train=x_train[:5000],t_train[:5000]
x_test,t_test=x_test[:1000],t_test[:1000]

max_epoch=20
network=SimpleConvNet( input_dim=(1, 28, 28), conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
                 hidden_size=100, output_size=10, weight_int_std=0.01)

trainer=Trainer(network, x_train, t_train, x_test, t_test,
                 epochs=max_epoch, mini_batch_size=100,
                 optimizer='Adam', optimizer_param={'lr': 0.001},
                 evaluate_sample_num_per_epoch=1000)

trainer.train()

#保存参数
network.save_param("params.pkl")
print("Save Network Parameters!")

#绘制图像
x=np.arange(max_epoch)
plt.plot(x,trainer.train_acc_list,marker='o',label='train',markevery=2)
plt.plot(x,trainer.test_acc_list,marker='s',label='test',markevery=2)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

3.结果展示

在这里插入图片描述

4.CNN的代表性网络

1).LeNet

  • 传统的CNN & LeNet的差异:

    ①.激活函数不同:LeNet使用sigmoid函数,传统的CNN网络使用的是Relu函数

    ②.原始的LeNet中使用子采样缩小中间数据的大小,传统的CNN网络主要使用Max池化。

2).AlexNet

  • LeNet & AlexNet的差异:

    ①.激活函数不同:LeNet使用sigmoid函数,AlexNet使用Relu函数

    ②.使用进行局部正则化的LRN(Local Response Normalization)层

    ③.使用Dropout

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

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

相关文章

实时数仓Hologres新一代弹性计算组实例技术揭秘

作者&#xff1a;王奇&#xff08;花名慧青&#xff09; 阿里云Hologres研发 随着实时数仓在业务生产系统的普及&#xff0c;资源弹性、资源隔离等保障业务稳定性方面的技术需求开始变得越来越迫切。Hologres在保障业务方面持续优化核心技术竞争力&#xff0c;过去一年中&…

Facebook广告投放的正确姿势:玩转目标定位

如果你正在投放 Facebook广告&#xff0c;那么你一定有过这样的经历&#xff1a;明明设置了目标受众&#xff0c;但是广告却没有带来转化。在这方面&#xff0c;你可能忽略了一个很重要的因素——目标定位。想要打造高质量、高曝光率的 Facebook广告&#xff0c;如何才能成功实…

「TCG 规范解读」第7章 TPM工作组 TPM 总结

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…

90%的人都理解错了HTTP中GET与POST的区别

Get和Post是HTTP请求的两种基本方法&#xff0c;要说它们的区别&#xff0c;接触过WEB开发的人都能说出一二。 最直观的区别就是Get把参数包含在URL中&#xff0c;Post通过request body传递参数。 你可能自己写过无数个Get和Post请求&#xff0c;或者已经看过很多权威网站总结…

数据分析-2.必会的六大实用模型

对于刚刚接触数据分析的人来说&#xff0c;经常会有这样的困惑和疑问&#xff1a;数据分析究竟难不难&#xff1f;难的话难在哪&#xff1f;为什么有时候作分析不知道从何下手&#xff0c;只能眉毛胡子一把抓&#xff1f; 其实就连我这种已经在数据分析行业浸淫十几年的老油条…

JavaSE学习day9 集合(基础班结束)

1.ArrayList 集合和数组的优势对比&#xff1a; 长度可变 添加数据的时候不需要考虑索引&#xff0c;默认将数据添加到末尾 不能存基本数据类型。只能通过包装。 1.1ArrayList类概述 什么是集合 提供一种存储空间可变的存储模型&#xff0c;存储的数据容量可以发生改变 Ar…

shell脚本中那些关于时间的处理方案,你都掌握了吗?

文章目录前言一. linux中关于时间的命令有哪些&#xff1f;1.1 命令一: hwclock命令二&#xff1a;date的那些事二. 时间命令在脚本中的应用2.1 用date命令实现统计执行时间的脚本2.2 time命令3. sleep命令总结前言 大家好&#xff0c;我是互联网老辛&#xff0c;专注云原生领…

[Css]Grid属性简单陈列(适合开发时有基础的快速过一眼)

[css进阶]Grid属性简介 文章目录[css进阶]Grid属性简介典型需求网格容器的属性displaygrid-template-columns和grid-template-rowsgrid-template-areasgrid-templategrid-column-gap grid-row-gapgrid-gapjustify-itemsalign-itemsjustify-contentalign-contentgrid-auto-colum…

【面试题】ES6 如何将 Set 转化为数组

大厂面试题分享 面试题库后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库Set 是 ES6 中新增的一种集合类型&#xff0c;类似于数组&#xff0c;但其成员的值是唯一的&#xff0c;即不会重复。关于Set&#xff0c;可以阅…

Leaf说明

什么是Leafleaf是叶子的意思我们使用的Leaf是美团公司开源的一个分布式序列号(id)生成系统我们可以在Github网站上下载项目直接使用为什么需要Leaf上面的图片中是一个实际开发中常见的读写分离的数据库部署格式专门进行数据更新(写)的有两个数据库节点它们同时新增数据可能产生…

ThinkPHP5篮球培训报名系统

有需要请私信或看评论链接哦 可远程调试 ThinkPHP5篮球培训报名系统一 介绍 此篮球培训报名系统基于ThinkPHP5框架开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。用户可注册登录&#xff0c;充值&#xff0c;报名&#xff0c;反馈信息等&…

手摸手快速入门 正则表达式 (Vue源码中的使用)

vue2源码 在 vue2 源码的 src\compiler\parser\html-parser.js 文件中 里面有大量的正则表达式&#xff0c;如下图 可以看到非常的长&#xff0c;不是我说&#xff0c;就前几行&#xff0c;如果没有相关的 正则表达式 的工具&#xff0c;我可能就被劝退了&#x1f62d; 这里…

反沙箱CobaltStrike木马加载器分析

前言 近日&#xff0c;笔者参加了浙江护网&#xff0c;在攻击队停止攻击的那一天凌晨&#xff0c;Windows服务器被攻破大量失分&#xff0c;早晨溯源时拿到了这一份名为chrome.exe的木马样本。 木马HASH SHA256:7fbe93d7c29b4ea4ce918f3d16a74d2930120f44d00862bdc0a1f82899…

ubuntu server系统树莓派安装mysql8.0开启远程访问

文章目录前言博客一、安装mysql8.0二、创建一个远程访问的新用户三、在MySQL配置文件中启用远程访问。四、navicat15连接mysql8.0返回10061chatgpt回复前言 百度了半天没解决&#xff0c;问了下chatgpt成功解决了…… 博客 一、安装mysql8.0 确认MySQL 8.0服务器已安装并正在…

【ESP32-S3】Pycharm 使用 microPython 教程(避坑)

一、下载Pycharm等操作 1.百度云下载链接 链接&#xff1a;https://pan.baidu.com/s/1tkbMzS5B_v-Cn4WQlTqS3Q?pwd0108 提取码&#xff1a;0108 2.安装 按照压缩包中的教程来&#xff0c;你懂的。 二、配置microPython环境 1.安装 microPython 插件 1.1 File > Sett…

【云原生】k8s之Yaml文件详解

一、K8S支持的文件格式 kubernetes支持YAML和JSON文件格式管理资源对象。 JSON格式&#xff1a;主要用于api接口之间消息的传递YAML格式&#xff1a;用于配置和管理&#xff0c;YAML是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读 1、yaml和json的主…

企业级信息系统开发学习笔记1.5 初探Spring AOP

文章目录零、本讲学习目标一、Spring AOP&#xff08;一&#xff09;AOP基本含义&#xff08;二&#xff09;AOP基本作用&#xff08;三&#xff09;AOP与OOP对比&#xff08;四&#xff09;AOP使用方式&#xff08;五&#xff09;AOP基本概念二、提出游吟诗人唱赞歌任务&#…

手把手教你做插件(2)模块大串联

0&#xff0c;前言 这篇文章笔记比较简略&#xff0c;大部分的操作都是和上一篇文章重复了&#xff0c;建议先看上一节文章&#xff0c;直达电梯&#xff1a;UE4 手把手教你做插件&#xff08;1&#xff09; 从代码引用插件_asiwxy的博客-CSDN博客UE4 手把手教你创建插件https:…

Windows10神州网信政府版麦克风、摄像头的使用

Windows10神州网信政府版默认麦克风摄像头是禁用状态&#xff0c;此禁用状态符合版本规定。 在录课和直播过程中&#xff0c;如果需要使用麦克风和摄像头的功能&#xff0c;可以这样更改&#xff1a; 1、鼠标右键点击屏幕左下角的开始菜单图标&#xff0c;选择windows中的“运…

[6/101] 101次软件测试面试之经典面试题剖析

01、自我介绍答&#xff1a;大家好&#xff0c;我是一名软件测试工程师&#xff0c;但我更喜欢称自己为“软件bug捕手”。我相信&#xff0c;软件测试工程师的使命就是让软件更加健壮、更加可靠、更加美好。我们就像是一群“特警”&#xff0c;在黑暗的代码中寻找漏洞和缺陷&am…