深度学习入门——神经网络的学习

前言

这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程。

为了使神经网络能进行学习,将导入损失函数这一指标

为了找出尽可能小的损失函数的值,本章我们将介绍利用了函数斜率的梯度法

从数据中学习

本章将介绍神经网络的学习,即利用数据决定参数值的方法,并用Python 实现对MNIST手写数字数据集的学习。

数据驱动

数据是机器学习的核心

机器学习的方法中,由机器从收集到的数据中找出规律性。与从零开始想出算法相比,这种方法可以更高效地解决问题,也能减轻人的负担

将图像转换为向量时使用的特征量仍是由人设计的

对于不同的问题,必须使用合适的特征量(必须设计专门的特征量),才能得到好的结果

image-20240714200307968

神经网络直接学习图像本身,连图像中包含的重要特征量也都是由机器来学习的。

[!IMPORTANT]

深度学习有时也称为端到端机器学习(end-to-end machine learning)。这里所说的端到端是指从一端到另一端的意思,也就是从原始数据(输入)中获得目标结果(输出)的意思

神经网络的优点是对所有的问题都可以用同样的流程来解决:都是通过不断地学习所提供的数据,尝试发现待求解的问题的模式

与待处理的问题无关,神经网络可以将数据直接作为原始数据,进行“端对端”的学习。

训练数据和测试数据

机器学习中,一般将数据分为训练数据和测试数据两部分来进行学习和实验等。首先,使用训练数据进行学习,寻找最优的参数;然后,使用测试数据评价训练得到的模型的实际能力

泛化能力是指处理未被观察过的数据(不包含在训练数据中的数据)的能力

只对某个数据集过度拟合的状态称为过拟合(over fitting)。避免过拟合也是机器学习的一个重要课题

损失函数

神经网络以某个指标为线索寻找最优权重参数。神经网络的学习中所用的指标称为损失函数(loss function)。这个损失函数可以使用任意函数,但一般用均方误差交叉熵误差等。

均方误差 mean squared error

image-20240714202539921

def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)

交叉熵误差 cross entropy error

image-20240714203122566

式(4.2)实际上只计算对应正确解标签的输出的自然对数

def cross_entropy_error(y, t):
    delta = 1e-7 #方式概率为0时 log输出负无穷大 -inf
    return -np.sum(t * np.log(y + delta))

mini-batch学习

计算损失函数时必须将所有的训练数据作为对象

image-20240714204010002

如果以全部数据为对象求损失函数的和,则计算过程需要花费较长的时间。

神经网络的学习是从训练数据中选出一批数据(称为mini-batch, 小批量),然后对每个mini-batch 进行学习。比如,从60000 个训练数据中随机选择100 笔,再用这100 笔数据进行学习。这种学习方式称为mini-batch 学习。

随机选择指定个数数据的代码

import sys, os
sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = \
	load_mnist(normalize=True, one_hot_label=True)
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000, 10)

train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]

使用np.random.choice()可以从指定的数字中随机选择想要的数字

mini-batch 的损失函数是利用一部分样本数据来近似地计算整体,用随机选择的小批量数据(mini-batch)作为全体训练数据的近似值。

mini-batch版交叉熵误差的实现

同时处理单个数据和批量数据(数据作为batch 集中输入)两种情况的函数:

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + 1e-7)) / batch_size

当监督数据是标签形式(非one-hot表示)

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

疑难点

np.log(y[np.arange(batch_size),t])有些不好理解

如果可以获得神经网络在正确解标签处的输出,就可以计算交叉熵误差

y还是一个二维数组,表示神经网络的输出,并不是一个单纯的标签,[0.1,0.05,...,0.01]

所以y[np.arange(batch_size),t]可以直接按照y的顺序取出t所指的位置的概率


为何要设定损失函数

“为什么要导入损失函数呢?”
以数字识别任务为例,我们想获得的是能提高识别精度的参数,特意再导入一个损失函数不是有些重复劳动吗?

在进行神经网络的学习时,不能将识别精度作为指标。因为如果以识别精度为指标,则参数的导数在绝大多数地方都会变为0。

识别精度对微小的参数变化基本上没有什么反应,即便有反应,它的值也是不连续地、突然地变化。作为激活函数的阶跃函数也有同样的情况。出于相同的原因,如果使用阶跃函数作为激活函数,神经网络的学习将无法进行。

而sigmoid 函数,如图4-4 所示,不仅函数的输出(竖轴的值)是连续变化的,曲线的斜率(导数)也是连续变化的。sigmoid 函数的导数在任何地方都不为0。这对神经网络的学习非常重要。得益于这个斜率不会为0 的性质,神经网络的学习得以正确进行

image-20240714220423001

数值微分 numerical differentiation

导数

image-20240716105116022

# 不好的实现示例
def numerical_diff(f, x):
    h = 10e-50
    return (f(x+h) - f(x)) / h

>>> np.float32(1e-50)
0.0

h使用了10e-50这个微小值,会产生舍入误差,因省略小数的精细部分的数值而造成的计算结果上的误差

def numerical_diff(f, x):
    h = 1e-4 # 0.0001
    return (f(x+h) - f(x-h)) / (2*h)

以x 为中心,计算它左右两边的差分,所以也称为中心差分(而(x + h) 和x之间的差分称为
前向差分)

[!IMPORTANT]

利用微小的差分求导数的过程称为数值微分(numerical differentiation)。

而基于数学式的推导求导数的过程,则用“解析性”(analytic)一词,称为“解析性求解”或者“解析性求导”。比如,y = x^2 的导数,可以通过dy/dx=2x解析性地求解出来。因此,当x = 2时,y的导数为4。解析性求导得到的导数是不含误差的“真的导数”

数值微分的例子

image-20240716112806657

def function_1(x):
	return 0.01*x**2 + 0.1*x

>>> numerical_diff(function_1, 5)
0.1999999999990898
>>> numerical_diff(function_1, 10)
0.2999999999986347
# 与解析解相比,0.2,0.3,误差小到基本上可以认为是相等的

image-20240716113201278

偏导数

image-20240716113307274

def function_2(x):
    return x[0]**2 + x[1]**2
    # 或者return np.sum(x**2)	

把有多个变量的函数的导数称为偏导数
∂ f ∂ x 0 \frac{\partial f}{\partial x_0} x0f
偏导数和单变量的导数一样,都是求某个地方的斜率

偏导数需要将多个变量中的某一个变量定为目标变量,并将其他变量固定为某个值

梯度

( ∂ f ∂ x 0 , ∂ f ∂ x 1 ) (\frac{\partial f}{\partial x_0},\frac{\partial f}{\partial x_1}) (x0f,x1f)

由全部变量的偏导数汇总而成的向量称为梯度(gradient)

def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x) # 生成和x形状相同的数组
    for idx in range(x.size):
        tmp_val = x[idx]
        # f(x+h)的计算
        x[idx] = tmp_val + h
        fxh1 = f(x)
        # f(x-h)的计算
        x[idx] = tmp_val - h
        fxh2 = f(x)
        
        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val # 还原值
    return grad

image-20240716152300944

画是元素值为负梯度的向量

梯度呈现为有向向量(箭头)。观察图4-9,我们发现梯度指向函数f(x0,x1) 的“最低处”(最小值),就像指南针一样,所有的箭头都指向同一点。其次,我们发现离“最低处”越远,箭头越大。

虽然图4-9 中的梯度指向了最低处,但并非任何时候都这样。实际上,梯度会指向各点处的函数值降低的方向。更严格地讲,梯度指示的方向是各点处的函数值减小最多的方向A。这是一个非常重要的性质,请一定牢记!

梯度法

一般而言,损失函数很复杂,参数空间庞大,我们不知道它在何处能取得最小值。而通过巧妙地使用梯度来寻找函数最小值(或者尽可能小的值)的方法就是梯度法

梯度表示的是各点处的函数值减小最多的方向,无法保证梯度所指的方向就是函数的最小值或者真正应该前进的方向。实际上,在复杂的函数中,梯度指示的方向基本上都不是函数值最小处

[!NOTE]

函数的极小值、最小值以及被称为鞍点(saddle point)的地方,梯度为0

虽然梯度法是要寻找梯度为0 的地方,但是那个地方不一定就是最小值(也有可能是极小值或者鞍点)

此外,当函数很复杂且呈扁平状时,学习可能会进入一个(几乎)平坦的地区,陷入被称为“学习高原”的无法前进的停滞期。

通过不断地沿梯度方向前进,逐渐减小函数值的过程就是梯度法(gradient method)。

梯度法是解决机器学习中最优化问题的常用方法,特别是在神经网络的学习中经常被使用

[!IMPORTANT]

根据目的是寻找最小值还是最大值,梯度法的叫法有所不同

寻找最小值的梯度法称为梯度下降法(gradient descent method),寻找最大值的梯度法称为梯度上升法(gradient ascent method)。

但是通过反转损失函数的符号,求最小值的问题和求最大值的问题会变成相同的问题,因此“下降”还是“上升”的差异本质上并不重要。

一般来说,神经网络(深度学习)中,梯度法主要是指梯度下降法。

image-20240716154350208

η表示更新量,在神经网络的学习中,称为学习率(learning rate)

学习率决定在一次学习中,应该学习多少,以及在多大程度上更新参数

每一步都按式(4.7)更新变量的值,通过反复执行此步骤,逐渐减小函数值。

学习率需要事先确定为某个值,比如0.01 或0.001。一般而言,这个值过大或过小,都无法抵达一个“好的位置”。在神经网络的学习中,一般会一边改变学习率的值,一边确认学习是否正确进行了。

def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x

参数f 是要进行最优化的函数,init_x 是初始值,lr 是学习率learningrate,step_num 是梯度法的重复次数。

image-20240716155031003

设定合适的学习率是一个很重要的问题

[!IMPORTANT]

像学习率这样的参数称为超参数

相对于神经网络的权重参数是通过训练数据和学习算法自动获得的,学习率这样的超参数则是人工设定的。

一般来说,超参数需要尝试多个值,以便找到一种可以使学习顺利进行的设定。

神经网络的梯度

这里所说的梯度是指损失函数关于权重参数的梯度

image-20240716161654747
∂ L ∂ w 11 \frac {\partial L}{\partial w_{11}} w11L
表示当w11稍微变化时,损失函数L关于发生多大变化,重点是,4.8两式形状相同

以一个简单的神经网络为例,来实现求梯度的代码

import sys, os
sys.path.append(os.pardir)
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient
class simpleNet:
    def __init__(self):
        self.W = np.random.randn(2,3) # 用高斯分布进行初始化
        def predict(self, x):
            return np.dot(x, self.W)
        def loss(self, x, t):
            z = self.predict(x)
            y = softmax(z)
            loss = cross_entropy_error(y, t)
            return loss
>>> net = simpleNet()
>>> print(net.W) # 权重参数
[[ 0.47355232 0.9977393 0.84668094],
[ 0.85557411 0.03563661 0.69422093]])
>>>
>>> x = np.array([0.6, 0.9])
>>> p = net.predict(x)
>>> print(p)
[ 1.05414809 0.63071653 1.1328074]
>>> np.argmax(p) # 最大值的索引
2
>>>
>>> t = np.array([0, 0, 1]) # 正确解标签
>>> net.loss(x, t)
0.92806853663411326

#这里的参数W是一个伪参数,因为numerical_gradient(f,x)会在内部执行f(x), 为了与之兼容而定义了f(W)
>>> def f(W):
... return net.loss(x, t)
...
#lambda 写法 f = lambda w: net.loss(x, t)
>>> dW = numerical_gradient(f, net.W)
>>> print(dW)
[[ 0.21924763 0.14356247 -0.36281009]
[ 0.32887144 0.2153437 -0.54421514]]

求出神经网络的梯度后,接下来只需根据梯度法,更新权重参数即可。

[!IMPORTANT]

为了对应形状为多维数组的权重参数W,这里使用的numerical_gradient()和之前的实现稍有不同

学习算法的实现

这里我们来确认一下神经网络的学习步骤,顺便复习一下这些内容

  • 前提
    • 神经网络存在合适的权重和偏置,调整权重和偏置以便拟合训练数据的过程称为“学习”。神经网络的学习分成下面4 个步骤。
  • 步骤1(mini-batch)
    • 从训练数据中随机选出一部分数据,这部分数据称为mini-batch。我们的目标是减小mini-batch 的损失函数的值。
  • 步骤2(计算梯度)
    • 为了减小mini-batch 的损失函数的值,需要求出各个权重参数的梯度。梯度表示损失函数的值减小最多的方向。
  • 步骤3(更新参数)
    • 将权重参数沿梯度方向进行微小更新。
  • 步骤4(重复)
    • 重复步骤1、步骤2、步骤3。

神经网络的学习按照上面4 个步骤进行,因为这里使用的数据是随机选择的mini batch 数据,所以又称为随机梯度下降法(stochastic gradient descent)。

“对随机选择的数据进行的梯度下降法”

深度学习的很多框架中,随机梯度下降法一般由一个名为SGD的函数来实现。

2层神经网络的类

import sys, os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient
class TwoLayerNet:
    def __init__(self, input_size, hidden_size, output_size,
                 weight_init_std=0.01):
        # 初始化权重
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)
    def predict(self, x):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        return y
    # x:输入数据, t:监督数据
    def loss(self, x, t):
        y = self.predict(x)
        return cross_entropy_error(y, t)
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        t = np.argmax(t, axis=1)
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
    # x:输入数据, t:监督数据
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        
        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        
        return grads

image-20240716170458363

如何设置权重参数的初始值这个问题是关系到神经网络能否成功学习的重要问题。后面我
们会详细讨论权重参数的初始化,这里只需要知道,权重使用符合高斯分布的随机数进行初始化,偏置使用0 进行初始化

[!IMPORTANT]

numerical_gradient(self, x, t)基于数值微分计算参数的梯度。下一章,我们会介绍一个高速计算梯度的方法,称为误差反向传播法

用误差反向传播法求到的梯度和数值微分的结果基本一致,但可以高速地进行处理

mini-batch的实现

import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
# 超参数
iters_num = 10000  # 适当设定循环的次数
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []

for i in range(iters_num):
    # 获取mini-batch
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    # 计算梯度
    grad = network.numerical_gradient(x_batch, t_batch)
    #grad = network.gradient(x_batch, t_batch) #高速版

    # 更新参数
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]

    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)

image-20240716202400330

基于测试数据的评价

根据图4-11 呈现的结果,我们确认了通过反复学习可以使损失函数的值逐渐减小这一事实

过拟合是指,虽然训练数据中的数字图像能被正确辨别,但是不在训练数据中的数字图像却无法被识别的现象。过拟合需要避免

[!IMPORTANT]

epoch是一个单位

一个epoch表示学习中所有训练数据均被使用过一次时的更新次数

import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

(x_train, t_train), (x_test, t_test) = \ load_mnist(normalize=True, one_hot_
                                                    label = True)
train_loss_list = []
train_acc_list = []
test_acc_list = []
# 平均每个epoch的重复次数
iter_per_epoch = max(train_size / batch_size, 1)

# 超参数
iters_num = 10000
batch_size = 100
learning_rate = 0.1

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

for i in range(iters_num):
    # 获取mini-batch
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    # 计算梯度
    grad = network.numerical_gradient(x_batch, t_batch)
    # grad = network.gradient(x_batch, t_batch) # 高速版!
    
    # 更新参数
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
        
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    # 计算每个epoch的识别精度
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

image-20240716204206126

小结

• 机器学习中使用的数据集分为训练数据和测试数据。
• 神经网络用训练数据进行学习,并用测试数据评价学习到的模型的泛化能力
• 神经网络的学习以损失函数为指标,更新权重参数,以使损失函数的值减小。
• 利用某个给定的微小值的差分求导数的过程,称为数值微分。
• 利用数值微分,可以计算权重参数的梯度。
• 数值微分虽然费时间,但是实现起来很简单。下一章中要实现的稍微复杂一些的误差反向传播法可以高速地计算梯度

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

筑梦未来,精准构建:Chief Architect Premier X10 for Mac,首席建筑师的专业之选

Chief Architect Premier X10 for Mac,是建筑设计领域的一款顶尖软件,专为追求卓越设计与精准构建的用户量身打造。它融合了先进的3D建模技术与直观的操作界面,让设计师能够轻松实现创意与现实的完美融合。 这款软件提供了丰富的设计工具与资…

大数据技术基础

一、大数据平台 1.大数据平台方案步骤: ①市场上有哪些大数据平台 ②硬件、系统、业务增长等方面 ③方案是否通过 通过后:按照一期目标投入 先虚拟环境部署联系,再实际部署 《大数据架构介绍》《Hadoop架构解析》《Hadoop集群规划》 《H…

第九课:服务器发布(静态nat配置)

一个要用到静态NAT的场景,当内网有一台服务器server1,假如一个用户在外网,从外网访问内网怎么访问呢,无法访问,这是因为外网没办法直接访问内网,这时候需要给服务器做一个静态NAT。 静态NAT指的是给服务器…

[k8s源码]4.informer

Informer 是 client-go 库中的一个核心组件,它提供了一种高效的方式来监视 Kubernetes 集群中资源的变化。Informer 通过 Watch 机制与 API Server 建立长连接,初次同步时会获取资源的完整列表,之后只接收增量更新,大大减少了网络流量。 使用informer可…

【概率论三】参数估计

文章目录 一. 点估计1. 矩估计法2. 极大似然法1. 似然函数2. 极大似然估计 3. 评价估计量的标准2.1. 无偏性2.2. 有效性2.3. 一致性 三. 区间估计1. 区间估计的概念2. 正态总体参数的区间估计 参数估计讲什么 由样本来确定未知参数参数估计分为点估计与区间估计 一. 点估计 所…

EE trade:强平和爆仓的区别

在金融交易市场中,杠杆交易的引入,让投资者可以用少量的资金撬动更大的头寸,获取更大的收益。然而,杠杆交易也带来了更大的风险,一旦市场波动,投资者可能会面临强平或爆仓的风险。了解强平和爆仓的区别&…

51单片机(STC8H8K64U/STC8051U34K64)_RA8889驱动大屏_硬件SPI4_参考代码(v1.3)

单片机实际不限,这里采用的STC最新、主推的型号,比如STC8H8K64U、STC8051U34K64进行实验测试,您可以换用不同型号。目前测试这两个系列,显示速度均相当不错,软件设计也是极为简单。各篇文章下方均提供源码供参考下载。…

ctfshow-web入门-php特性(web123、web125、web126)

目录 1、web123 2、web125 3、web126 1、web123 要求存在 post 传入 CTF_SHOW 和 CTF_SHOW.COM,不能存在 get 传入 fl0g。 正则匹配过滤掉了一些符号,符合则会执行 eval 函数,其中 c 来自 post 传入的 fun。 我们先说非预期解&#xff0c…

.net dataexcel 脚本公式 函数源码

示例如: ScriptExec(""sum(1, 2, 3, 4)"") 结果等于10 using Feng.Excel.Builder; using Feng.Excel.Collections; using Feng.Excel.Interfaces; using Feng.Script.CBEexpress; using Feng.Script.Method; using System; using System.Collections.Gen…

js执行机制----事件循环

前言 问题 一般情况下,我们都认为js是顺序执行的 但是遇到下列情况 setTimeout(function(){console.log(定时器开始啦) });new Promise(function(resolve){console.log(马上执行for循环啦);for(var i 0; i < 10000; i){i 99 && resolve();} }).then(function(…

Hadoop-32 ZooKeeper 分布式锁问题 分布式锁Java实现 附带案例和实现思路代码

章节内容 上节我们完成了&#xff1a; ZooKeeper的Leader选举机制ZooKeeper的选举过程ZooKeeper的ZAB协议 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&#xff0…

如何让LabVIEW程序框图的图标简化,从而节省空间?

再点击选项 取消掉箭头所示的√即可。 这样就可以将生成的图标从下面所示&#xff1a; 变成简化的图标&#xff0c;如下所示&#xff1a;

QT实现滑动页面组件,多页面动态切换

这篇文章主要介绍了Qt实现界面滑动切换效果&#xff0c;对大家的学习或工作具有一定的参考借鉴价值&#xff0c;需要的朋友可以参考下。 一、简述 一个基于Qt的动态滑动页面组件。 二、 设计思路 1、自定义StackWidget类&#xff0c;继承自QWidget&#xff0c;实现一个堆叠…

持续集成04--Jenkins结合Gitee创建项目

前言 在持续集成/持续部署&#xff08;CI/CD&#xff09;的旅途中&#xff0c;Jenkins与版本控制系统的紧密集成是不可或缺的一环。本篇“持续集成03--Jenkins结合Gitee创建项目”将引导如何将Jenkins与Gitee&#xff08;一个流行的Git代码托管平台&#xff09;相结合&#xff…

SpringBoot使用本地缓存——Caffeine

SpringBoot使用本地缓存——Caffeine 缓存&#xff0c;想必大家都用过&#xff0c;将常用的数据存储在缓存上能在一定程度上提升数据存取的速度。这正是局部性原理的应用。之前用的缓存大多是分布式的&#xff0c;比如Redis。使用Redis作为缓存虽然是大多数系统的选择&#xf…

vue2学习笔记7 - Vue中的MVVM模型

MVVM Model-View-viewModel是一种软件架构模式&#xff0c;用于将用户界面&#xff08;View&#xff09;与业务逻辑&#xff08;Model&#xff09;分离&#xff0c;并通过ViewModel进行连接和协调。MVVM模式的目标是实现视图与模型的解耦&#xff0c;提高代码的可读性、可维护…

力扣Hot100之两数之和

解法一&#xff1a; 双层循环暴力求解&#xff0c;先在数组的一个位置定住然后在这个位置的后续位置进行判断&#xff0c;如果两个数加起来等于目标和那么就返回 class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:for i,num in enumerate(num…

react Jsx基础概念和本质

什么是jsx jsx是JavaScript和XML(HTML)的缩写&#xff0c;表示在js代码中编写HTML模板结构&#xff0c;它是react中编写UI模板的方式 const message this is message function App(){return (<div><h1>this is title</h1>{message}</div>) } jsx优…

js补环境系列之剖析:原型、原型对象、实例对象三者互相转化(不讲废话、全是干货)

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 思考下&#xff1a;js补环境中&#xff0c;什么场景会用到原型、原型对象、实例对象&#xff1f; 举…

通过docker构建基于LNMP的WordPress项目

目录 1.准备nginx 2.准备mysql 3.准备php 4.构建各镜像 5.运行wordpress 1、项目环境&#xff1a; 1.1 &#xff08;1&#xff09;公司在实际的生产环境中&#xff0c;需要使用Docker 技术在一台主机上创建LNMP服务并运行Wordpress网站平台。然后对此服务进行相关的性能…