机器学习理论入门---线性回归从理论到实践

线性回归是机器学习里面最简单也是最常用的算法,理解了线性回归的推导之后对于后续的学习有很大帮助,所以我决定从这里开始深入学习相关的机器学习模型。
本篇首先从矩阵求导开始切入,然后介绍一次线性回归的推导,再到代码实现。本人也是入门哈,有什么错误望轻喷。

求导

简单求导规则

首先先不管模型本身,对于机器学习来说,各种矩阵的运算非常重要,而求导又是最基础的,所以就放在最前面。(主要参考)

矩阵和标量之间的求导:形状取决于矩阵(矩阵包括向量),无论是标量对矩阵求导,矩阵对标量求导,形状都和矩阵一致,每个元素都是标量求导。

矩阵对矩阵求导:矩阵求导本质上是把向量化为标量的过程,假设存在变量 y → m × 1 \overrightarrow{y}_{m×1} y m×1和对应的函数 f ( y → ) n × 1 f(\overrightarrow{y})_{n×1} f(y )n×1,其中每一行为 f i ( y → ) f_{i}(\overrightarrow{y}) fi(y ),此时存在求导法则:
∂ f ( y → ) ∂ y → = ( ∂ f 1 ( y → ) ∂ y 1 ⋯ ∂ f 1 ( y → ) ∂ y m ⋮ ⋱ ⋮ ∂ f n ( y → ) ∂ y 1 ⋯ ∂ f n ( y → ) ∂ y m ) n × m \frac{ \partial f(\overrightarrow{y}) }{ \partial \overrightarrow{y} }=\begin{pmatrix} \frac{ \partial f_{1}(\overrightarrow{y}) }{ \partial {y_1} } & \cdots & \frac{ \partial f_{1}(\overrightarrow{y}) }{ \partial {y_m} }\\ \vdots & \ddots & \vdots\\ \frac{ \partial f_{n}(\overrightarrow{y}) }{ \partial {y_1} } & \cdots & \frac{ \partial f_{n}(\overrightarrow{y}) }{ \partial {y_m} } \end{pmatrix}_{n×m} y f(y )= y1f1(y )y1fn(y )ymf1(y )ymfn(y ) n×m
在矩阵运算中,由于存在行列的概念,因此需要区别两种布局方式:分子布局分母布局
分子布局:分子布局指的是求导后的矩阵行数和分子对应行数相等
分母布局:分母布局指的是求导后的矩阵行数和分母对应行数相等
显然,上面的公式为分子布局,因为分子 n n n ( f n × 1 ) (f_{n×1}) (fn×1),最终结果也是 n n n行,下面都采用分子布局计算。

下面有两个常用的偏导式,可以用于加速导数计算:
假设存在方阵 A A A和向量 y → \overrightarrow{y} y

  1. ∂ A y → ∂ y → = A T \frac{ \partial A\overrightarrow{y} }{ \partial \overrightarrow{y} }=A^T y Ay =AT
  2. ∂ y → T A y → ∂ y → = A y → + A T y → \frac{ \partial \overrightarrow{y}^TA\overrightarrow{y} }{ \partial \overrightarrow{y} }=A\overrightarrow{y}+A^T\overrightarrow{y} y y TAy =Ay +ATy (二次型相关),若 A A A是对称矩阵,那么可以以进一步化为 2 A y → 2A\overrightarrow{y} 2Ay
    简单证明一下1:

在这里插入图片描述
这一段证明可能有点抽象,建议可以自己写一下感受一下,我简单说一下我的想法。可以看到,最后对应的每个导数,上面是 f f f,下面是 y y y,先看第一行,第一行都是对 y 1 y_1 y1求导,从左往右分别是 f 1 f_{1} f1 f m f_m fm,因此m对 y 1 y_1 y1的求导从左往右是 ∂ f 1 ( y → ) ∂ y 1 , ∂ f 2 ( y → ) ∂ y 2 . . . , ∂ f m ( y → ) ∂ y m \frac{ \partial f_1(\overrightarrow{y}) }{ \partial {y_1} },\frac{ \partial f_2(\overrightarrow{y}) }{ \partial {y_2} }...,\frac{ \partial f_m(\overrightarrow{y}) }{ \partial {y_m} } y1f1(y ),y2f2(y )...,ymfm(y ),这其实就对应了 A A A第一列的系数,也就是 a 11 , a 21 , . . . , a m 1 a_{11},a_{21},...,a_{m1} a11,a21,...,am1,相当于把列转为了行。
还有一个2我没证过,也可以尝试一下熟悉熟悉规则。

链式求导

对于一般的标量函数 f ( g ( x ) ) f(g(x)) f(g(x)),此时对于 f ( x ) f(x) f(x) x x x存在链式求导法则:
∂ f ( g ( x ) ) ∂ x = ∂ f ( x ) ∂ g ( x ) ∂ g ( x ) ∂ x \frac{ \partial f(g(x)) }{ \partial x }=\frac{\partial f(x)}{\partial g(x)}\frac{\partial g(x)}{\partial x} xf(g(x))=g(x)f(x)xg(x)
但是对于矩阵来说,由于矩阵的乘法一般不满足交换律,此时链式求导法则如下:
∂ f ( g ( x ) ) ∂ x = ∂ g → ∂ x → ∂ f ∂ g → \frac{ \partial f(g(x)) }{ \partial x }=\frac{\partial \overrightarrow{g}}{\partial \overrightarrow{x}}\frac{\partial f}{\partial \overrightarrow{g}} xf(g(x))=x g g f
可以看到,大求导在后面,小求导在前面,直接面向自变量的在第一个。
下面给出一个例题,假设存在 x k + 1 = A x k + B u k x_{k+1}=Ax_k+Bu_{k} xk+1=Axk+Buk,令 J = x k + 1 T x k + 1 J=x^T_{k+1}x_{k+1} J=xk+1Txk+1(J其实是一个控制系统里面的损失函数),计算 ∂ J ∂ u k \frac{ \partial J }{ \partial u_{k} } ukJ

在这里插入图片描述
注意其中有一步,也就是 ∂ J ∂ x k + 1 \frac{ \partial J }{ \partial x_{k+1} } xk+1J,这个利用了二次型转换,相当于中间的 A A A就是单位阵 I I I I I I是一定对称的,所以结果是 2 x k + 1 2x_{k+1} 2xk+1

线性回归

原理

首先是第一个模型:线性回归。可以先考虑一元线性回归,这个是最简单的回归模型,表达式为 y = w x + b y=wx+b y=wx+b,需要计算的就是两个参数: w , b w,b w,b
用矩阵计算的相对要复杂一点(但是扩展性强),可以先考虑用标量求导的方式来证明,如下:(参考证明)
在这里插入图片描述
这个就计算到了求出导数这一步,没算出解析解,后面代码是用的梯度下降算的。
下面用矩阵的方式求解,这种方法的扩展性比较强(多元线性回归的证明也是这样,本质一样),假设 x , y x,y x,y均为 n × 1 n×1 n×1矩阵,假设结果为一列 m × 1 m×1 m×1向量 t t t,前面 m − 1 m-1 m1个为 w w w,最后一个是 b b b,经过证明,其结果为:
t = ( x T x ) − 1 y T x t=(x^Tx)^{-1}y^Tx t=(xTx)1yTx
下面是简单的证明,这里还是一元一次线性回归的:
在这里插入图片描述
最后的 x T y x^Ty xTy是一个 2 × 1 2×1 2×1向量,导数是 2 × 1 2×1 2×1的,分别对应 w , b w,b w,b的偏导,当偏导都为0,也就对应了最小值,所以本质还是解方程,解两个偏导方程)。有的证明会把损失函数前面加上 1 / 2 n 1/2n 1/2n,也就是除掉样本数,但本质其实是一样的,不影响求解。
我一开始其实写错了,上面打叉的就是第一次写错的,还是后面写代码的时候才发现的(所以动手实践一下还是很重要的)。
注意:损失函数本身是一个标量( 1 × 1 1×1 1×1),其导数是 2 × 1 2×1 2×1的,对应了求解的参数矩阵。

代码

一元一次线性回归

首先是基于解方程的思路,也就是直接算出数值解(对应于上面基于矩阵的证明部分)。

def my_lr(x,y):
    n = x.shape[0]
    x = np.hstack((x,np.ones((n,1)))) # 加上一列n
    # print(x.shape) # (n,2)
    t = (inv(dot(x.T,x))@x.T@y) # t=(xx^T)^(-1)y^Tx, @表示矩阵乘法,dot也是矩阵乘法
    w,b=t[0][0],t[1][0]
    # 下面进行拟合
    y_hat = w*x[:,0]+b # 取第一列,第二列是增广的
    return y_hat

上面的本质在解方程,下面用梯度下降的思路求解:
简单概括梯度下降就是: w = w − α ▽ w=w-α▽ w=wα,其中 ▽ ▽ 是梯度(多元情况的导数), ▽ = ∂ J ∂ w ▽=\frac{ \partial J}{ \partial w } =wJ α α α是学习率。
首先是基于标量方式求解各个变量的,也就是用 w , b w,b w,b这样的形式写出来,然后求梯度迭代:

def my_gradi_lr(x,y):
    print('--------------基于标量的梯度下降--------------')
    # 定义损失函数:T(w,b)=1/(2m)*∑(yi_hat-yi),yi_hat=wxi-b
    # w=w-α(1/m*∑xi(yi_hat-yi)) b=b-α(1/m*∑(yi_hat-yi))
    m = x.shape[0]
    w,b=0,0
    alpha=0.01
    epochs=50
    for i in range(epochs):
        y_hat = w*x+b # 计算拟合值
        w=(w-alpha*(x.T@(y_hat-y))/m)# ∑xi(yi_hat-yi)/m
        b=(b-alpha*(np.sum(y_hat-y))/m) # ∑(yi_hat-yi)/m
        y_hat = w * x + b # 更新新y_hat计算loss
        loss = 1/(2*m)*(y_hat-y).T@(y_hat-y) # 1/(2m)*∑(yi_hat-yi)
        print('epoch:%d loss:%.4f'%(i+1,loss))
    # print(w,b)
    return w*x+b

推导的过程就是链式求导,因为是完全标量的,还是比较简单的。
下面是基于矩阵的,推导过程跟前面矩阵求解里面是一致的,如果一定要比较的话,上面那种标量直观一点,但是参数多了就不方便了,下面的适合更多参数,比如之后应该会提到的多元线性回归。

def my_mat_gradi_lr(x,y):
    print('--------------基于矩阵的梯度下降--------------')
    n = x.shape[0]
    x = np.hstack((x,np.ones((n,1)))) # 加上一列n
    alpha=0.01
    epochs=50
    t=np.zeros((2,1)) # 系数矩阵
    for i in range(epochs):
        gradi = 2*(x.T@x@t-x.T@y)/(2*n)
        t=t - alpha * gradi
        loss = (t.T@x.T@x@t+y.T@y-2*y.T@x@t)/(2*n)
        print('epoch:%d loss:%.4f' % (i + 1, loss))
    return x@t # y_hat

这两种是完全一样的,具体可以看迭代的过程,loss的变化是一模一样的。
下面是运算的结果,sklearn(sklearn model)的可以视为标答,数值解(my model)的答案是和sklearn的是一样的,说明结果是对的。两个grendient都是对应的梯度下降的解法,两种迭代的算法是一样的,从迭代过程中的loss可以看出(可以运行一下代码),由于迭代次数次数比较少(50),所以和精确解还是有一定距离。
在这里插入图片描述
下面是代码:

# 一元一次线性回归

import numpy as np
from matplotlib import pyplot as plt
from numpy import dot
from numpy.linalg import inv
from sklearn.linear_model import LinearRegression
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

def my_lr(x,y):
    n = x.shape[0]
    x = np.hstack((x,np.ones((n,1)))) # 加上一列n
    # print(x.shape)
    t = (inv(dot(x.T,x))@x.T@y) # t=(xx^T)^(-1)y^Tx, @表示矩阵乘法,dot也是矩阵乘法
    # print(t.shape)
    # print(t)
    w,b=t[0][0],t[1][0]
    # print(w,b)
    # 下面进行拟合
    y_hat = w*x[:,0]+b # 取第一列,第二列是增广的
    return y_hat

def skle_lr(x,y):
    model = LinearRegression()
    model.fit(x, y)
    return model.predict(x)

# 利用梯度下降求解(基于标量链式求导)
def my_gradi_lr(x,y):
    print('--------------基于标量的梯度下降--------------')
    # 定义损失函数:T(w,b)=1/(2m)*∑(yi_hat-yi),yi_hat=wxi-b
    # w=w-α(1/m*∑xi(yi_hat-yi)) b=b-α(1/m*∑(yi_hat-yi))
    m = x.shape[0]
    w,b=0,0
    alpha=0.01
    epochs=50
    for i in range(epochs):
        # print('epoch:',i+1)
        y_hat = w*x+b # 计算拟合值
        w=(w-alpha*(x.T@(y_hat-y))/m)# ∑xi(yi_hat-yi)/m
        b=(b-alpha*(np.sum(y_hat-y))/m) # ∑(yi_hat-yi)/m
        y_hat = w * x + b # 更新新y_hat计算loss
        loss = 1/(2*m)*(y_hat-y).T@(y_hat-y) # 1/(2m)*∑(yi_hat-yi)
        print('epoch:%d loss:%.4f'%(i+1,loss))
    # print(w,b)
    return w*x+b

# 采用矩阵形式的梯度下降
def my_mat_gradi_lr(x,y):
    print('--------------基于矩阵的梯度下降--------------')
    n = x.shape[0]
    x = np.hstack((x,np.ones((n,1)))) # 加上一列n
    alpha=0.01
    epochs=50
    t=np.zeros((2,1)) # 系数矩阵
    for i in range(epochs):
        # print(x.T.shape)
        # print(t.shape)
        # print((x.T@x@t).shape)
        gradi = 2*(x.T@x@t-x.T@y)/(2*n)
        t=t - alpha * gradi
        loss = (t.T@x.T@x@t+y.T@y-2*y.T@x@t)/(2*n)
        print('epoch:%d loss:%.4f' % (i + 1, loss))
    return x@t # y_hat


if __name__ == '__main__':
    x=np.array([1,2,3,4,5,6]).reshape(-1,1)
    y=np.array([2,4,7,9,10,11]).reshape(-1,1) # 列向量
    my_y_hat = my_lr(x,y)
    skle_y_hat = skle_lr(x,y)
    my_gradi_y_hat = my_gradi_lr(x,y)
    my_mat_gradi_y_hat = my_mat_gradi_lr(x,y)
    plt.scatter(x,y)
    plt.plot(x,my_y_hat,linestyle='--',label='my model')
    plt.plot(x,my_gradi_y_hat,linestyle='-.',label='my grendient')
    plt.plot(x,my_mat_gradi_y_hat,linestyle='-.',label='my mat grendient')
    plt.plot(x,skle_y_hat,linestyle='-',label='sklearn model')
    plt.legend()
    plt.show()

多元一次线性回归

对于多元线性回归,其实本质上还是一样的,下面提供多元线性回归的代码,使用的例子是一个三元一次线性回归。具体的证明和上面矩阵证明的过程是完全一致的,解析求解的方式(求逆)的代码和一元的一样,用梯度下降方式的就改一点点就可以(其实一元也就是特殊的多元,下面这段代码也可以用)。

# 采用矩阵形式的梯度下降
def my_mat_gradi_lr(x,y):
    print('--------------基于矩阵的梯度下降--------------')
    n = x.shape[0]
    n_samples = x.shape[1]
    x = np.hstack((x,np.ones((n,1)))) # 加上一列n
    alpha=0.01
    epochs=50
    t=np.zeros((n_samples+1,1)) # 系数矩阵,唯一变化的地方
    for i in range(epochs):
        gradi = 2*(x.T@x@t-x.T@y)/(2*n)
        t=t - alpha * gradi
        loss = (t.T@x.T@x@t+y.T@y-2*y.T@x@t)/(2*n)
        print('epoch:%d loss:%.4f' % (i + 1, loss))
    return x@t # y_hat

这里因为不方便画出图像,就画出y的比较了,每个点都是实际点,线对应的都是拟合值,x轴表示第几个点
在这里插入图片描述
从结果中看解析解和sklearn得到是一样的,就是精确解,梯度下降还是差的比较多,是因为迭代次数少(50),提高到5000或者改变学习率(alpha)就可以得到更好的结果。
在这里插入图片描述
贴出代码:

# 多元线性回归
import numpy as np
from matplotlib import pyplot as plt
from numpy import dot
from numpy.linalg import inv
from sklearn.linear_model import LinearRegression

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

def my_lr(x,y):
    n = x.shape[0]
    x = np.hstack((x,np.ones((n,1)))) # 加上一列n
    # print(x.shape)
    t = (inv(dot(x.T,x))@x.T@y) # t=(xx^T)^(-1)y^Tx, @表示矩阵乘法,dot也是矩阵乘法
    # 下面进行拟合
    print('my lr:',t)
    return x@t


def skle_lr(x,y):
    model = LinearRegression()
    model.fit(x, y)
    print('sklearn:',model.coef_)
    return model.predict(x)


# 采用矩阵形式的梯度下降
def my_mat_gradi_lr(x,y):
    print('--------------基于矩阵的梯度下降--------------')
    n = x.shape[0]
    n_samples = x.shape[1]
    x = np.hstack((x,np.ones((n,1)))) # 加上一列n
    alpha=0.01
    epochs=50
    t=np.zeros((n_samples+1,1)) # 系数矩阵
    for i in range(epochs):
        gradi = 2*(x.T@x@t-x.T@y)/(2*n)
        t=t - alpha * gradi
        loss = (t.T@x.T@x@t+y.T@y-2*y.T@x@t)/(2*n)
        # print('epoch:%d loss:%.4f' % (i + 1, loss))
    print('matrix grandient:',t.T)
    return x@t # y_hat


if __name__ == '__main__':
    x=np.array([[1,2,3,4,5,6],[2,3,5,7,8,9],[8,7,5,4,2,1]]).T
    y=np.array([2,4,7,9,10,11]).reshape(-1,1) # 列向量
    my_y_hat = my_lr(x,y)
    skle_y_hat = skle_lr(x,y)
    my_mat_gradi_y_hat = my_mat_gradi_lr(x,y)
    plot_x = np.arange(y.shape[0])
    plt.scatter(plot_x,y)
    plt.plot(plot_x,my_y_hat,linestyle='--',label='my model')
    plt.plot(plot_x,my_mat_gradi_y_hat,linestyle='-.',label='my mat grendient')
    plt.plot(plot_x,skle_y_hat,linestyle='-',label='sklearn model')
    plt.legend()
    plt.show()

❀❀❀完结撒花❀❀❀

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

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

相关文章

【八股】Redisson分布式锁

Redisson分布式锁 主要了解了Redisson分布式锁实现的三个功能: 1.可重入 -> 防止死锁 2.可重试(i.e. 非阻塞获取锁) 3.自动续约 1. 可重入 原理: 利用Redis的Hash结构,记录了使用当前锁的线程id和重用次数&#…

零基础小白,如何入门计算机视觉?

目录 前言 计算机视觉技术学习路线 基础知识 1. 数学基础 2. 编程基础 3. 图像处理基础 基础算法与技术 1. 特征提取与描述符 2. 图像分割与对象检测 3. 三维重建与立体视觉 机器学习与深度学习 1. 机器学习基础 2. 深度学习 高级主题与应用 1. 高级机器学习与深度学习 2. 计算…

Linux之 USB驱动框架-USB总线核心和主控驱动(4)

一、USB设备描述符 一个USB设备描述符中可以有多个配置描述符,即USB设备可以有多种配置;一个配置描述符中可以有多个接口描述符,即USB设备可以支持多种功能(接口);一个接口描述符中可以有多个端点描述符。 …

【YOLOv8改进[Neck]】使用BiFPN助力V8更优秀

目录 一 BiFPN(双向特征金字塔网络) 1 BiFPN 2 EfficientDet 二 使用BiFPN助力模型更优秀 1 整体修改 2 配置文件 3 训练 其他 一 BiFPN(双向特征金字塔网络) BiFPN(双向特征金字塔网络, 2020)用于特征融合层。 官方论文地址:https://arxiv.org…

445. 两数相加 II

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外,这两个数字都不会以零开头。 示例1: 输入:l1 [7,2,4,3], l2 [5,6,4]…

什么地推网推拉新副业平台最值得推荐? 赚取互联网第一桶金

随着互联网的发展,新型行业层出不穷。其中地推网推拉新作为互联网行业具有收入高、门槛低、时间自由等优势,一部分人从中嗅到了商机,开始纷纷接触并加入了进来。但还是有一部分人对于地推网推拉新的了解很少,不知道如何才能加入其…

这个“高端智库”落地上塘,数字生活商务社区企航俱乐部正式成立

4月10日上午,由浙江省跨境电子商务产业联盟指导,上塘街道党工委、办事处主办,上塘街道数字生活商务社区、运河(国际)跨境电子商务园承办的上塘街道数字生活商务社区企航俱乐部成立仪式暨助力跨境电商高质量发展首期交流…

计算机考研都将采用408!?

这个根本不可能,高考还没做到全国统一考试呢 每个学校对于计算机招生的需求是不一样的,比如清华大学,专业课912,算的上是最难的计算机专业课了,那他为什么搞这么难啊,还不是因为那群敢考清华的卷王们太变态…

Re65:读论文 GPT-3 Language Models are Few-Shot Learners

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文全名:Language Models are Few-Shot Learners ArXiv网址:https://arxiv.org/abs/2005.14165 2020 NeurIPS:https://papers.nips.cc/paper/2020/hash/1457c0d6bfcb49674…

2024阿里云4核8G服务器租用优惠价格700元一年

阿里云4核8G服务器租用优惠价格700元1年,配置为ECS通用算力型u1实例(ecs.u1-c1m2.xlarge)4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选,CPU采用Intel(R) Xeon(R) Platinum处理器,阿里云优惠 aliyunfuwuqi…

MSSQL 命令行操作说明 sql server 2022 命令行下进行配置管理

说明:本文的内容是因为我在导入Access2019的 *.accdb 格式的数据时,总是出错的背景下,不得已搜索和整理了一下,如何用命令行进行sql server 数据库和用户管理的方法,作为从Access2019 直接导出数据到sql server 数据库…

1997-2022年各省技术市场发展水平数据(原始数据+计算过程+计算结果)

1997-2022年各省技术市场发展水平数据(原始数据计算过程计算结果) 1、时间:2000-2022年 2、来源:国家统计局、统计年鉴 3、范围:30省 4、指标:技术市场成交额、国内生产总值、技术市场发展水平 5、计算…

牛仔裤哪个牌子质量好?平价高品质牛仔裤推荐

一条好的裤子,不仅穿着能够显瘦显高,同时质量也更加耐洗耐穿。但大家却极少能够选择到这些质量好的裤子。其实这都是因为目前市面上的裤子品牌实在太多,而且还有不少质量不够出色的品牌混杂在其中。那么要选什么品牌的裤子才好呢?…

iPad手绘+Ai二合一课程,Procreate+Mj+SD零基础到精通(10节视频课)

课程内容: 1 系统课 AI辅助设计流-从零进阶轻松驾驭AI设计,mp4 2 商务沟通阶段 ChatGPT Midjourney-聊天机器人 项目调研资料收集 ,mp4 3_商务沟通阶段 ChatGPT_Midjourney-Midjourney基础 界面初识初步设置 .mp4 4_商务沟通阶段 ChatGPT_Midjourney-Midjourney…

软件测试入门学习笔记

系统测试流程规范 一.研发模型 1.瀑布模型 从可行性研究(或系统分析)开始,需求 2.增量迭代模型 3.敏捷开发模型 二.质量模型

你觉得职场能力重要还是情商重要?

职场能力和情商都是职业成功的关键因素,它们在不同的情境和角色中扮演着不同的作用。很难简单地说哪一个更重要,因为它们通常是相辅相成的。 职场能力包括专业技能、知识水平、解决问题的能力、工作效率、创新思维等。这些能力是完成工作任务、达成职业目…

P1278 单词游戏 简单搜索+玄学优化

单词游戏 传送门 题目描述 Io 和 Ao 在玩一个单词游戏。 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。 游戏可以从任何一个单词开始。 任何单词禁止说两遍,游戏中只能使用给定词典中含有…

Vue2 —— 学习(七)

目录 一、TodoList 案例(第一版) (一)组件化编码流程 1.实现静态组件 2.显示动态数据 (二)增加元素 (三)多选框状态确定 (四)删除元素 (五…

「 网络安全常用术语解读 」漏洞利用交换VEX详解

漏洞利用交换(Vulnerability Exploitability eXchange,简称VEX)是一个信息安全领域的标准,旨在提供关于软件漏洞及其潜在利用的实时信息。根据美国政府发布的用例(PDF),由美国政府开发的漏洞利用交换(VEX)使供应商和用…

ARM_day8:温湿度数据采集应用

1、IIC通信过程 主机发送起始信号、主机发送8位(7位从机地址1位传送方向(0W,1R))、从机应答、发数据、应答、数据传输完,主机发送停止信号 2、起始信号和终止信号 SCL时钟线,SDA数据线 SCL高电平,SDA由高到低——起始信号 SC…