当前位置: 首页 > news >正文

一文入门:机器学习

机器学习

什么是机器学习

机器学习是一门让计算机在没有明确编程的情况下,从数据中自动学习经验并提升性能的学科。
通俗的讲就是:
如果你想教电脑识别猫,你不需要写规则去判断“猫有几个耳朵、几个胡须”;
而是给它大量猫和非猫的图片,让它自己总结出“这像是猫”的特征。
机器学习 ≈ “让计算机通过看例子来自己总结经验”
就像人类一样:
如果你小时候看了很多苹果和橘子,你就能分辨两者;
机器学习系统也是这样,只不过它“看”的是数据,用数学算法来学习。
完整学术定义:
一个计算机程序在完成某项任务T时,能够通过经验E使得其在性能评估指标P上的表现得到提升,那么我们就说这个程序从经验E中学习了。
正如机器学习的定义所言,我们机器是具体怎么学习的呢?有什么样的学习方法呢?

机器学习的分类

根据数据和监督方式的不同:
机器学习主要可以分为三大类监督学习、无监督学习、强化学习。
还有半监督学习,但我们只是入门就不作过深的讨论。
那么先让我们看看什么是监督学习吧

监督学习

什么是监督学习呢?
监督学习是你给计算机输入数据和正确答案,让它自己学习规则,之后能对新数据做出准确预测,这就叫做监督学习。
通俗的讲就像小时候你做练习题,有老师给你标准答案(监督),你做多了之后,也能自己判断对不对。
常见的监督学习算法有回归算法和分类两种,
回归(预测连续值)
分类(预测离散类别)

回归算法

线性回归模型

是现在使用最多的回归算法。
比如我们事先给机器很多卖房的数据,面积多大,卖多少钱,然后让机器生成线性回归模型,然后我们就可以随机输入面积,得到可以卖多少钱的最优解。

训练集

用于训练模型的数据集
监督学习中的训练集包括输入特征和输出目标。
一般情况都是先用训练集训练模型,再让模型预测。
训练模型就是将训练集(包括输入特征和输出目标)提供给您的学习算法。然后监督学习算法会产生函数,比如y=kx
我们就可以利用这个函数来进行预测,x称为输入或者输入特征,y则是模型的输出,因为输出是预测,因此有时y是估计值y^,当字母只是y的时候,则指的是目标,是实际值。

测试集

用来评估模型性能,

验证集

我们往往还会额外划分一部分数据作为验证集。
验证集的作用是:帮助我们调整超参数(比如学习率、正则化参数、特征数量等),避免模型过拟合。
验证集不同于测试集:测试集是最后评估模型的,而验证集可以在训练过程中多次使用。

代价函数

在完成线性回归模型这项工作中,必须要做的事是构建一个成本/代价函数
它是“让模型学会预测”的关键。
为什么要有它呢?
它就像一个“评分机制”,告诉我们当前的模型预测得好不好。模型的目标就是让这个评分(误差)越小越好。我们可以通过成本函数来找到最佳参数。
线性回归模型中的一种典型的成本函数是均方误差(MSE):
如下图所示:
在这里插入图片描述
在这里插入图片描述

这个公式这里如果不多一个2还好理解和方差类似实际上就是最小二乘法,多了一个2的意思是为了让计算更简洁。
由上述内容我们可以知道:
我们可以通过大量的样本x,y,找到在不同参数下的代价函数的最小值,在该值下的参数也就是最合适的,从而得出最合适的函数模型,进而得出最可靠的目标。
训练的核心过程:
我们假设模型形式 f(x;θ)
不断尝试不同的参数 θ
找到让 成本函数 J(θ) 最小的参数组合:
(对于线性回归模型参数就是;w,b y=wx+b)
这其实就是所谓的训练模型(Model Training)
当知道了可以通过成本函数来找参数最小值,那么如何快速的找到呢?
这里有一种方法叫做梯度下降算法。

梯度下降算法

实质就是求导,找最值。跟高数里面的梯度是一样的。
在这里插入图片描述
每一轮迭代中,我们同时计算所有参数的偏导数(梯度),然后再 一起更新参数的值。
如下图所示:
在这里插入图片描述
在这里插入图片描述
重复这个过程若干次(几百到几千轮),直到:
梯度变得很小(说明接近最低点)
或者损失几乎不再减少
此时我们就得到了:
一组让损失函数最小的 w,b
这里我们会看到一个新的重要的参数叫做学习率α

学习率

学习率是用来控制参数更新的幅度,它决定了优化过程中参数的收敛速度与稳定性。
如果学习率过小,则每次迭代更新幅度极小,模型收敛速度缓慢,可能陷入“停滞”。
如果学习率过大,则更新可能跨越最优点,导致 震荡(oscillation) 或 发散(divergence)。
如果我们把学习率设置的非常小,然后去不断的训练模型,如果此时在代价函数中每次没有减少而是有时增加,那么通常意味着代码里面有某处错误的存在。

我们已经知道了更新参数的方法,但是什么时候可以知道,当参数为xx的时候,代价函数已经是最小值了呢?

判断梯度下降是否收敛

我们是通过检查梯度下降是否收敛,从而判断在此时的参数时,可以得到代价函数的最小值。
这里我们用学习曲线来判断什么时候收敛
学习曲线通过记录损失函数值 J(θ) 的变化情况来判断什么时候收敛
横坐标表示训练次数,纵坐标表示代价函数。
在这里插入图片描述
用公式来表示就是

在这里插入图片描述
说明收敛

正规方程法

梯度下降是迭代法,而正规方程法是一种解析解,直接求最优参数:
在这里插入图片描述
多元线性回归同样适用正规方程法。

多元线性回归

我们之前讲了一种简单的线性回归算法,可是如果是多元的呢?它和单元的方法还是一样吗?在了解之前我们需要熟悉一下概念。

多类特征

多类特征指的是输入数据中包含多种类型的特征。
例如:
年龄(数值型)
性别(类别型)
教育程度(类别型)
工作经验(数值型)
兴趣爱好(文本型或类别型)
放在回归方程中就是下图:
在这里插入图片描述

向量化

向量化可以加快执行速度
把原来需要使用循环对数据逐个处理的操作,改写为矩阵或向量运算的形式,从而利用底层线性代数库的高效实现,提升计算效率。
向量化一般会用到python中的NumPy这个线性代数库

f = np.dot(w,x)+b

我们可以看一下向量化和非向量化有什么不同:
非向量化:
在这里插入图片描述
或者:
在这里插入图片描述
向量化:
在这里插入图片描述
这提高了计算效率,也看起来更简洁。
当我们清楚了上面的概念后就很容易理解多元线性回归的模型了。

多元代价函数

在这里插入图片描述

多元线性回归和单变量线性回归在原理上一样,只是输入特征由单个变量扩展为多个变量。利用向量化表达,可以把多个特征统一写成矩阵运算形式,从而提升计算效率。

多元线性回归的梯度下降算法

在这里插入图片描述
在这里插入图片描述
多元的梯度下降比起单元来讲参数更多,可能会不好求,于是就有了一种叫做特征放缩的技术,它让梯度下降更快,从而更容易的找到最小的代价函数。

特征放缩技术

特征缩放(Feature Scaling),是指: 将原始特征数据进行统一变换,使它们落在相近的尺度范围内(例如 0 到 1,或 -1 到 1)。
我们在收集到数据不同特征的可能数值差别非常大,如果我们不做放缩,某些算法(比如梯度下降、KNN、SVM)会:认为“面积”更重要,只因为它的数值更大;
在计算距离或更新权重时,变化小的特征被忽略;从而导致训练慢、收敛困难,甚至发散。
那么怎么进行放缩呢?

常见的特征放缩方法

最大绝对值放缩:
在这里插入图片描述

在这里插入图片描述
这会把每个特征缩放到 [-1, 1] 范围内
常用于稀疏数据(如文本分类中的 TF-IDF 向量)
最小-最大放缩 (归一化)
在这里插入图片描述
将特征值缩放到 [0, 1] 区间内(或其他自定义范围)
适用于:

  • 图像像素(通常 0255 → 01)
  • 对结果范围敏感的模型(如神经网络)

标准化
也叫做Z-score 归一化
在这里插入图片描述
μ:该特征的均值
σ:该特征的标准差
结果:变换后特征的均值为0,标准差为1
适用于:

  • 有负数、或特征可能是正态分布的情况
  • 线性模型、逻辑回归、神经网络等都常用

实际上在机器学习中不仅仅会对特征进行缩放处理,还可以转换和构造,它们的目的都是让模型更好地学习。
由此引出特征工程的概念:

特征工程

特征工程:从原始数据中提取、转换、优化特征,使得机器学习模型能更好地学习和预测
特征工程都做什么呢?

步骤作用
1. 缺失值处理填补空值(如均值填充、0填充等)
2. 特征选择删除无用的特征,保留重要特征
3. 特征提取 / 构造从原始数据中提取有用的新特征(如时间提取出“年/月/日”)
4. 特征编码把分类数据变成数字(如 One-hot 编码)
5. 特征缩放(归一化)把特征变到统一的范围(如 [0,1]),加快模型训练速度

我们之前的学习都是围绕这一次方来讲的,但是不可避免的会有多次方的回归模型。
那么就让我们看看什么是多项式回归

多项式回归

多项式回归 = 用更高阶的特征(比如 x2,x3x^2, x^3x2,x3)来拟合非线性数据的一种方法,依然是线性,但是是用线性方法,做非线性拟合。
而在多项式回归中 特征缩放技术也就更重要,因为平方,立方等待会让数值范围差距非常大

分类

分类与回归算法不同的是输出的变量y之只能取少数几个可能值中的一个,而不再是任意值。
常见的分类有

常见的分类

二元分类
binary classification
经常指定为 no/yes,0/1,false/true

将两个类别分隔开的一条线或面。
那么解决分类问题,特别是二分类,也有属于它们的算法,最广泛使用的分类算法就是:逻辑回归了。

逻辑回归

逻辑回归来解决分类,虽然也叫回归,但是它还是用来解决分类的问题,只不过是翻译和历史的原因
在这里插入图片描述

在这里插入图片描述
这里有一个新的概念:

决策边界

模型把不同类别“分开”的那条线(或面)
它把“预测为类别1”和“预测为类别0”的区域分割开。
在这里插入图片描述

逻辑回归的代价函数

我们可以先思考一下如果逻辑回归的代价函数和线性回归的代价函数用同一种,会出现什么样的情况呢?
在这里插入图片描述
由图可知,逻辑回归的代价函数不能跟线性回归的代价函数是一致的
应该建立一个新的代价函数,如下图所示
这是单个样本的代价函数
在这里插入图片描述
可以对这个代价函数进行简化
在这里插入图片描述
但我们训练的不是一个样本,而是一堆样本
因此最终的总的代价函数是:
在这里插入图片描述
那么我们如何找逻辑回归的代价函数的最小值呢?同样也用到梯度下降的方法。

梯度下降解决逻辑回归

实际上类似线性回归的方法,如下图所示:
在这里插入图片描述
只不过代价函数不一样,思路是相通的。
以上就是我们学习的两大类模型,但是在实际过程中,通过上面的这些算法就一定正确吗,会不会出现一些问题或误差呢?
一般来讲我们用偏差和方差来解释模型的误差,与之对应的还有欠拟合和过拟合。

偏差和方差

那什么是偏差
在线性回归中,如果我们用一条直线去拟合,效果不好,那么就说这个算法有高偏差或者说它对这个数据集欠拟合。如果用一个四阶多项式去拟合,那么我们就称它为高方差,或者说是过拟合。
在这里插入图片描述
通过判断训练集和验证集结果的高低来判断是偏差还是方差
如下图所示 训练集误差和验证集误差都高的是高偏差,训练集误差低,验证集误差高的是方差。
在这里插入图片描述
画图解释就是:
在这里插入图片描述
那么高偏差和高拟合不能同时出现吗?

在理论上,高偏差(欠拟合)和高方差(过拟合)通常是对立的,难以同时发生;但在某些实际复杂情况中,它们可以“局部共存”。

高偏差和高方差一般被视为模型容量的两个极端:

  • 模型很简单 → 容易 欠拟合(高偏差),不会过拟合。
  • 模型很复杂 → 容易 过拟合(高方差),但不容易欠拟合。

所以,大多数情况下,一旦你遇到高偏差问题(比如训练误差都很大),那通常不太可能有高方差(因为训练集都学不好了,怎么可能过拟合呢)。

但实际中可以“局部共存”:

在真实的数据和模型中,可能出现一些混合情况,比如:

  • 数据非常稀疏或噪声大:某些区域数据少,模型容易过拟合;其他区域模型过于简单,又容易欠拟合。
  • 模型有结构缺陷或训练不充分:比如神经网络网络结构很复杂但没有训练好,结果两头都不沾。
  • 优化不到位:参数没有收敛,导致既没学好训练数据(高偏差),也对验证集不稳定(高方差)。

这时你会发现:
训练误差并不低(说明有高偏差),但 验证误差和训练误差之间差距也很大(说明有高方差)

过拟合

过拟合是指模型在训练数据上表现得非常好,但在新数据(测试集)上表现很差。也就是说模型“记住了”训练数据的细节(甚至是噪声),而没有学到数据背后的通用规律
无论是:

  • 线性回归(Linear Regression);
  • 逻辑回归(Logistic Regression);
  • 神经网络(Neural Network);
  • 决策树随机森林XGBoost等;

都可能会出现过拟合现象。
模型复杂程度越高,越容易过拟合
过拟合的表现是高方差,高方差意味着模型对训练集很敏感,哪怕训练数据稍微变化一下,模型预测结果就会波动很大。

解决过拟合的方法

方案一:
收集更多的数据范围,数据集。即足够多的训练样本
但并不是总会有很的数据因此也要用其他方法
方案二:
尽可能少的用特征,即多项式尽可能少
选择最合适的特征集,也被称为特征选择
但少使用特征,也可能影响结果,因为其他特征也会对结果有影响
方案三:
是正则化,正则化是一种更温和地减少某些特征影响的方法,而不是像方案二一样去除它们。
鼓励学习算法缩小参数值而不是消除参数
通常会正则化参数w1-wn,而不对b进行正则化

欠拟合

模型在训练集和测试集上都表现差
原因可能是:
特征不足(信息不够)
模型过于简单(比如只用一条直线拟合明显非线性数据)
训练不充分
欠拟合的表现是偏差,high bias,很好理解。
我们可以把机器学习理解为找到一个既不过拟合,又不欠拟合的模型
在决策边界上,过拟合和欠拟合所具有的不同特点,也能说明问题:
在这里插入图片描述

解决欠拟合的方法

增加更多特征
使用更复杂的模型(多项式、非线性模型)
减少正则化
我们发现不论是过拟合还是欠拟合都提到了正则化,那么就让我们来看看这种方法吧

正则化

常用来解决过拟合问题。
正则化(regularization)的核心思想就是在原有的代价函数(损失函数)中加入一个正则项(regularization term)以惩罚模型复杂度,从而减少过拟合。
通常是对所有的w进行惩罚,因为我们不知道哪一个影响的大:
在这里插入图片描述
加入正则化的代价函数:
在这里插入图片描述
lambda是正则化参数
正则化使得w不会取非常大的值,使得其过拟合。

正则化线性回归

正则化后的线性回归
在这里插入图片描述

正则化逻辑回归

和线性回归的正则化类似。
在这里插入图片描述

正则化和偏差或方差

我们知道可以对算法进行正则化以此来避免过拟合,那么很明显正则化就会对偏差或方差产生影响。
我们可以举一个例子如下图所示:
在这里插入图片描述
当正则化项的λ过大,那就会趋近一个常数,在图像上的表示就是一条直线,这很明显的是欠拟合。也就是高偏差。

当正则化项的λ过小,加上正则化和不加正则化就区别不大,当这个算法是一个高阶多项式的时候,自然就过拟合,高方差。

那么怎么找到合适的λ呢?方法类似用验证集照多项式的项数d
在这里插入图片描述
那么随着λ的变化,Jtrain训练集误差和Jcv交叉验证集误差会怎么变化?
如下图:
在这里插入图片描述

以上是我们监督学习中常见的两种算法大类:回归算法和分类算法。
但实际上还有一些算法,不仅仅可以用它来解决回归问题,还可以来解决分类问题。
这里我们就来介绍一种算法:决策树,泛指决策树家族

决策树

在学术界的关注不多,但是一种很实用的工具。
决策树包括分类分类树和回归树。
首先它可以用来解决分类问题。
决策树是一种常见的机器学习模型,形式上和数据结构中的树很像

  • 每个 内部节点 对应一个特征条件(例如 “年龄 > 30?”)。
  • 每个 分支 代表一种条件结果(是/否)。
  • 每个 叶子节点 给出预测结果(例如“通过/不通过”,“正类/负类”)。
  • 每次选择一个特征,将数据按特征值划分,使得每个子集“更纯”
  • 纯度可以用指标衡量,如:
    信息增益(Information Gain)
    基尼系数(Gini Index)
    增益率(Gain Ratio)
  • 递归划分直到满足停止条件(如达到最大深度或叶子节点足够纯)

在这里插入图片描述
那么我们怎么样能测量出具体的纯度呢?

测量纯度

拿识别猫的二分类作为例子,如果识别的所有数据都是猫,那么就称非常纯,
如果全都不是猫,那也非常纯,如果介于两者之间,那么如何量化这个纯度呢?
由此引出了一个名词叫做 **熵(即混乱程度)**它是衡量一个集合不纯度的指标
通常会根据数据集中的数据绘制出熵函数图像如下图所示:
p1表示在所有数据中目标数据所占的比例。
在这里插入图片描述

信息增益

在决策树中,熵的减少称为信息增益
可以通过判断信息增益的大小来判断纯度。
那么就引出下面的内容
在决策树中计算信息增益,从而决定在哪一个节点分裂哪一个特征
如果熵减小的太少,就可以选择不继续分裂,防止过拟合
信息增益具体是怎么计算的呢?
是用根节点的熵值减去下一个它的左右孩子节点的加权熵
在这里插入图片描述
信息增益越大 → 说明该特征划分效果越好 → 更适合作为节点。
现在我们知道了决策树中计算信息增益,从而决定在哪一个节点分裂哪一个特征,那么怎么从0构建一棵决策树呢?

构建决策树

在根节点上,尝试用每一个特征去划分数据集,看看划分之后子集的纯度(信息增益)提升多少,选择提供最高信息增益的特征进行分裂,选择这个特征之后,然后会根据选定的特征将数据集分成两个子集并创建左分支和右分支的树,并将训练示例发送到左分支或右分支。递归创建树,一直这样做直到满足停止标准,停止标准可能是当一个节点100%属于单一类别,也就是说熵达到0或者进一步分裂一个节点会导致树超过你设定的最大深度。

决策树处理连续有价值的特征

排序
对某一个连续特征(例如身高、价格、温度),先把所有样本在这个特征上的取值排序。
候选划分点(阈值)
如果有 n 个不同的取值,可以考虑相邻两个值的中点作为候选阈值。
比如特征取值 {2, 3, 7, 10},候选阈值就是:
在这里插入图片描述
划分数据集
对每一个候选阈值 t,把样本分成两部分:
左子集:特征值 ≤ t
右子集:特征值 > t
计算分裂后的纯度指标
常用的指标:
信息增益(ID3 用的)
选择最佳阈值
遍历所有候选阈值,找到使信息增益(或基尼指数下降最大)最大的阈值,用它来分裂节点。
递归构建子树
对左右子集重复上述过程,直到满足停止条件(如纯度足够高,或样本数量太少)。

独热编码解决分类特征

在机器学习中,大多数模型不能直接处理类别型(离散)特征。

比如特征:

颜色 = {红色, 绿色, 蓝色}

如果你把它简单映射成:

红色 = 1
绿色 = 2
蓝色 = 3

会引入错误的“大小关系”:模型可能误以为“蓝色 > 绿色 > 红色”。

但实际上颜色之间没有这种数值顺序。

用独热编码解决多值特征问题,其中一个特征值取1,取1的称为独热,其他取0。把每个类别转化为一组二进制特征,从而避免引入虚假的顺序关系。
经过独热编码变成三列:

红色  绿色  蓝色
1     0     0
0     1     0
0     0     1

传统决策树可以直接处理离散特征,不一定非要独热编码(比如 Weather={Sunny, Rain, Overcast} 可以直接分支)。但独热编码是解决离散特征的一种思路嘛
独热编码的思路同样适用于训练神经网络:
神经网络只能接受数值输入,不能直接理解“红色/蓝色”这种字符串。

  • 所以必须先做 独热编码(或者更常用的是 embedding 向量)。
  • 比如 NLP 里的词向量就是对独热编码的升级:每个词先独热,再通过嵌入层降维得到稠密表示。
回归树

用决策树解决回归问题,将决策树泛化为回归算法,以便它们可以来预测数值
回归树用 特征划分 → 子区间 → 在叶子节点输出一个预测值(通常是均值) 来完成回归任务。
从根节点开始,包含所有训练样本。
选择一个特征 + 阈值 来划分数据(比如“房屋面积 ≤ 100 平方米?”)。
左右子集分别继续递归划分。
叶子节点 的预测值是该节点内样本目标值的 平均值(或者更一般地是最小二乘意义下的最优常数)。
用类似“信息增益”的指标来选择划分点,但对于回归树我们通常用 均方误差 (MSE)方差减少
在分类树里,我们用 基尼指数 来衡量“纯度”。
在回归树里,我们用 平方误差(SSE)方差 来衡量“拟合好不好”。
平方误差类似信息增益,用方差衡量不确定性,分裂时计算 方差减少 = 父节点方差 - 子节点加权方差
如果这个减少值很大,就说明这个分裂很有价值。

吴恩达老师关于回归树的例子:在这里插入图片描述
他这里先以耳朵形状作为特征分类,然后又以面部形状作为特征分类,同一类别的动物的体重是在一个区间的,那么我们以后就可以通过识别一个动物的耳朵形状和面部形状来预测它的体重了!

使用多个决策树 (集成学习)

单棵决策树虽然直观,但有两个大问题:
容易过拟合:树可以一直分裂下去,把训练集记住,泛化能力差。
高方差:换一批数据,生成的树可能完全不同,结果不稳定。
解决方法就是:建多棵树,让它们“投票/加权”来提升稳定性和准确率
建造多个决策树的方法又被称为树的集成
为了构建树的集成,我们需要采用一种叫做放回采样的技术
放回抽样的技术有点像构建了一个新的数据集,但又有点不同,这个技术是构建树的集成的关键。
由使用多个决策树我们实际上可以引出一种机器学习方法–集成学习。

集成学习

什么是集成学习(Ensemble Learning)?
集成学习是一种机器学习方法,核心思想是:把多个模型(通常称为“基学习器”或“弱学习器”)结合起来,形成一个性能更强的模型。
如果只是随便训练几棵树,然后不做融合
那还不能算严格意义上的集成学习。
因为集成学习要求 多个基学习器的输出要被整合(投票、加权、平均、叠加等),最终形成一个比单一模型更强的整体。

基学习器(Base Learner)

一般是一些相对“弱”的模型(比如小决策树)。
集成后能得到比单个模型更好的性能。

集成方式

主要思想是“多模型 → 融合 → 强模型”。
常见的融合方式:投票、加权、平均、学习残差等。

集成学习的三大主流方法
Bagging(并行思想)

思路:对训练集做有放回采样,训练多个模型,最后投票/平均。
代表算法:随机森林(Random Forest)。
特点:降低方差,减少过拟合。

Boosting(串行思想)

思路:模型一个接一个地训练,后面的模型重点关注前面预测错的样本。
代表算法:XGBoost、LightGBM、CatBoost。
特点:降低偏差,提高精度。

Stacking(堆叠思想)

思路:先训练多个不同的基模型(可能是 SVM、决策树、神经网络等),再用一个“元学习器”来融合它们的输出。
特点:常用于比赛(比如 Kaggle),提升效果显著。

随机森林算法

随机森林算法是也是一种树集成算法,比使用单个决策树效果要好得多。
在学习随机森林算法之前我们需要先学一个叫做袋装决策树

袋装决策树

袋装决策树(Bagging Trees)

  • 核心思想
    • 通过 Bootstrap抽样(有放回抽样)来生成多个不同的训练集。
    • 在每个抽样训练集上训练一棵 完全独立的决策树
    • 最终通过 投票(分类问题)平均(回归问题) 得到预测结果。
  • 优点:减少单个决策树的过拟合,提高模型稳定性。
  • 缺点:树之间可能相关性比较高(尤其是特征数量不大时,很多树会在根节点选到相同的特征,导致差异性不足)。
    随机森林(Random Forest)
  • 在袋装的基础上增加了“特征子采样”
    • 在训练过程中,每个节点进行分裂时,不是从所有 n 个特征里挑选最优分裂特征,而是 随机挑选一个子集(大小为 k ),只允许在这 k 个特征中选择。
  • 好处
    • 增强了树之间的差异性(decorrelation)。
    • 降低了所有树都依赖于某几个强特征的风险。
    • 集成后的投票结果更加稳健,通常比单纯的Bagging效果更好。
XGBoost算法(极端梯度提升)

在讲XGBoost
它是最常用的决策树集成或决策树实现方法
XGBoost 属于 Boosting 系列算法,它和随机森林(Bagging 系列)最大的不同是:

  • Bagging:多棵树“并行”训练,彼此独立,最后投票/平均。
  • Boosting:多棵树“串行”训练,后一棵树会重点学习前一棵树做错的地方。

换句话说:

  • 随机森林 → 降低方差(通过并行多数投票提高稳定性)
  • XGBoost → 降低偏差(通过逐步修正错误提高精度)
工作流程
  1. 初始化:从训练数据开始,先训练第一棵树。
  2. 计算误差:看哪些样本被预测错了、误差大。
  3. 调整权重:给预测错的样本更高的权重,让下一棵树更关注它们。
  4. 生成下一棵树:训练新的树来“纠正”前面树的错误。
  5. 加权组合:将所有树的预测结果按照权重组合起来,形成最终预测。

这种方法就像“刻意练习”:每次都盯着没掌握的地方不断改进。

停止条件
  • 如果某个节点的样本数太少,或者分裂增益不足,算法会自动停止生长。
  • 避免生成过深、过复杂的树。
什么时候使用决策树

决策树和集成树通常在表格数据上表现良好,也称为结构化数据
意味着你的数据集看起来像一个巨大的电子表格时,可以考虑使用决策树
在使用决策树的时候,集成树用的多,其中XGBoost是最多的。
到这里我们对监督学习这个类别的学习也就差不多了。
接下来让我们学习无监督学习

在介绍无监督学习和强化学习之前,我们需要先了解一类更为强大的方法——深度学习。
深度学习几乎贯穿所有学习范式:无监督、监督,乃至强化学习,都离不开它的应用。
在这里我们对深度学习的介绍也是大概的,之后会单独写一篇文章来介绍深度学习:

深度学习

神经元和大脑

一个逻辑回归算法可以看作一个神经元

但实际上它是大脑中一个实际神经元的简化模型。

神经元可以看成一个小的机器学习算法。

神经网络

神经网络就是将神经元组合或连接在一起。

神经网络一开始是为了构建模仿大脑的软件

但现在更多的是用工程的思想来构建

深度学习和神经网络实际上意义非常接近

神经网络的发展实际上是曲折的

随着数据量的增加,导致了神经网络的兴起

神经网络(Neural Network)是受人脑启发而设计的一种算法结构,它是通过训练从数据中自动学习特征和规律,完成分类、回归等任务
一个典型的神经网络由以下三部分组成:

  1. 输入层(Input Layer)
    • 接收原始特征输入。
    • 每个输入节点表示一个特征。
  2. 隐藏层(Hidden Layers)
    • 一层或多层,完成复杂的计算。
    • 由多个神经元构成。
    • 每个神经元对前一层所有输出进行加权、加偏置、非线性激活。
    • 越多层,模型能力越强(也更容易过拟合)。
  3. 输出层(Output Layer)
    • 给出最终的预测结果,比如分类标签或回归值。
      在这里插入图片描述
      在这里插入图片描述
      由图可知:
      输入层,隐藏层:隐藏层输出一个激活向量,输出层:最终激活,最终预测。
      神经网络的一个非常好的特征是当你从数据中训练它时,你不需要明确决定哪些是特征,比如可负担性等等神经网络应该计算的特征,相反它会自己找出这个隐藏层中想要使用的特征。也就是自动学习特征。
      也是神经网络和机器学习的区别:

传统机器学习:我们必须手动提取特征,例如“可负担性 = 价格/收入”。

神经网络:它通过训练,自行学会如何组合输入特征,比如某个隐藏神经元可能学会了“房屋位置影响购买意愿”这样的抽象特征。
在这里插入图片描述这种多层的隐藏层的神经网络也被称为多层感知器。

那神经网络具体是怎么来实现自动找到哪些特征重要呢?

神经网络由很多层“神经元”组成,每个神经元都在做一件事:

把一堆输入乘上不同的权重,再加个偏置,经过激活函数,得到一个输出。
在这里插入图片描述图像识别的例子:
在这里插入图片描述

神经元层

输入层一般是第0层,接着第一层,第二层。。。
第二层的输入,是第一层的输出
下面的每一个神经元都是一个逻辑回归
在这里插入图片描述
下面是第二层:
在这里插入图片描述
最终的预测结果:
在这里插入图片描述
更复杂的神经网络:
在这里插入图片描述
由图可知:

知道 输入层激活值 a[0] 和网络的全部参数 Wb那就可以推理出每一层的激活值(正向传播,Forward Propagation)

正向传播(前向传播)

在这里插入图片描述
sigmoid激活函数
在这里插入图片描述
用于计算 预测结果
训练过程中会将中间变量(如 z[l],a[l]z^{[l]}, a^{[l]}z[l],a[l])缓存起来,用于 后续的反向传播(反向传播计算梯度)
到这里我们对深度学习有了一些浅显的认识,接着让我们看一些学深度学习不可缺少的工具

TensorFlow

深度学习常用框架,常用的还有PyTorch

Numpy是python中的线性代数库,可以用它来直接表示矩阵

TensorFlow是吴恩达创建的一种框架,它也可以实现

但两者还是存在区别的。

在NumPy,TensorFlow来表示矩阵。
x = np.array([[1,2,3],[4,5,6]])
x = np.array([[200],[17]])

张量

张量可以看成是“任意维度的数组”

它是标量、向量、矩阵的统称和推广

Python 中用 NumPyPyTorchTensorFlow 中定义的 arrayTensor,它内部都是张量结构。
在有了这些的铺垫,我们就可以更深的认识神经网络啦

dense layer

Dense Layer(密集层 / 全连接层,Fully Connected Layer) 是神经网络中最常见的一种层,它的主要特点是:

每一个神经元都和上一层的每一个神经元都有连接。

所以叫做 “Dense” —— 每一对节点之间都是“密密麻麻地”连接起来的。

创建一个层:

layer_1 = Dense(units=3,activation="sigmoid")
layer_2 = Dense(units=1,activation="sigmoid")

创建一个神经网络

创建一个神经网络让两个层串联起来

model = Sequential([layer_1,layer_2])
# 编译模型(指定优化器、损失函数、评估指标)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 训练模型
model.fit(x, y, epochs=50, verbose=0)  # verbose=0 不显示过程;你也可以用 verbose=1
# 准备一个新样本预测
x_new = np.array([[0.25, 0.35]])
y_pred = model.predict(x_new)
# 输出预测结果
print("预测概率:", y_pred)
print("预测标签:", 1 if y_pred[0][0] >= 0.5 else 0)
y=np.array([1,0,0,1])
model.compile(...) # 编译
# 训练模型
model.fit(x,y)
# 预测
model.predict(x_new)

在一个单层中的正向传播

下图是涉及到了两个层,传播的思路是一样的。
在这里插入图片描述

前向传播的一般实现

密集函数:参数有上一层的激活值,参数w,参数b,和激活函数。

def dense(a_in,W,b,g):units = W.shape[1]a_out = np.zeros(units)for j in range(units):w = W[:,j]z = np.dot(w,a_in) + b[j]a_out[j] = g(z)return a_out

高效化实现神经网络

神经网络能够被扩展的原因之一是因为神经网络可以矢量化。

它们可以使用矩阵乘法高效地实现

而gpu非常擅长进行非常大的矩阵乘法

那么这样的矢量化神经网络是如何实现的呢?

矢量化的实现:
在这里插入图片描述
一个基本的神经网络,也被称为多层感知器MLP

import tensorflow as tf
from tensorflow.keras.models import Sequential    
# 用于按顺序堆叠网络层
from tensorflow.keras.layers import Dense        
# Dense = 全连接层
from tensorflow.keras.losses import BinaryCrossentropy 
# 二分类交叉熵损失#构建模型:顺序堆叠 3 个 Dense 层
model = Sequential([Dense(units=25, activation='sigmoid',  input_shape=(x.shape[1],)),  # 隐藏层 1Dense(units=15, activation='sigmoid'),                              # 隐藏层 2Dense(units=1,  activation='sigmoid')                               # 输出层
])#编译模型:指定损失函数、优化器、指标
model.compile(optimizer='adam',                   # 常用自适应优化器loss=BinaryCrossentropy(),          # 二分类交叉熵损失metrics=['accuracy']                # 额外监控准确率
)#训练模型:x、y 是你的训练数据与标签
model.fit(x, y, epochs=100, batch_size=32)

它的底层就是逻辑回归的过程,而这些代码正是调框架库

常用的激活函数

通常一般我们都是用的激活函数是sigmoid,但有一个更有力的函数是ReLU,g(z),校验线性单元,它在训练深度网络时表现得更高效、更稳定。

还有一种叫做线性激活函数,也被称为没有激活函数。因为它对神经元输出不做任何变化。从左到右分别是线性,sigmoid,ReLU
在这里插入图片描述
之后还会讲一个叫做softmax的激活函数

为什么要选择激活函数

可以不用激活函数吗?

激活函数的作用是给神经网络引入非线性,使模型具有更强的表示能力。如果没有激活函数,整个神经网络就变成了一个线性模型,再多层也没有意义。

如何选择激活函数

根据目标和Y的不同,对于二分类 sigmoid是最自然的。

比如股票的每天的涨跌,我们可以选择线性激活函数。因为y可以为正负

如果y只能选择正值,那么用ReLU,
在这里插入图片描述

多类别分类

多类别分类指的是可能具有多个输出类别的分类问题。不止0和1
可以用混合的
在这里插入图片描述

重要的激活函数softmax

Softmax 是一种常用在 多类别分类任务 中的 激活函数,作用是将一组实数(logits)转换成一个 概率分布,这些概率表示每个类别被预测为正确的可能性。

Softmax 只能用于互斥分类问题(一个输入只属于一个类别)

如果是 多标签分类(一个样本可能属于多个类别),就不要用 softmax,而要用 sigmoid + binary_crossentropy
在这里插入图片描述
当y=1,2两个的时候,只有两个类别的时候,Softmax实际上变成了sigmoid函数,因此Softmax回归函数是逻辑回归函数的广义形式。
在这里插入图片描述

构建多条款分类的神经网络

采用softmax回归模型,并将其作为神经网络的输出层(softmax输出层)
在这里插入图片描述
用tensorflow如何表示呢?
在这里插入图片描述
这是一种表示方法,还有其他的表示方法。
这种方法得出的结果是有误差的

Softmax的改进实现

一种是先在隐藏层直接使用softmax激活函数

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.optimizers import Adam# 定义模型
model2 = Sequential([Dense(25, activation='relu', input_shape=(20,)),  # 第一层,25个神经元,ReLU激活,输入20维特征Dense(15, activation='relu'),                     # 第二层,15个神经元,ReLU激活Dense(10, activation='softmax')                 # 第三层,10个神经元,softmax激活,输出概率分布
])# 编译模型
model2.compile(optimizer=Adam(),                                 # 优化器用Adamloss=SparseCategoricalCrossentropy(),             # 损失函数默认 from_logits=False,直接使用概率计算交叉熵metrics=['accuracy']                           # 评估指标用准确率
)
  • 优点:代码直观,预测时直接得到概率
  • 缺点:数值稳定性略逊于第一种(尤其类别很多时)

这种是模型输出 softmax 概率,损失函数默认 from_logits=False

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.optimizers import Adam# 定义模型
model1 = Sequential([Dense(25, activation='relu', input_shape=(20,)),# 第一层,25个神经元,ReLU激活,输入20维特征Dense(15, activation='relu'),                     # 第二层,15个神经元,ReLU激活Dense(10)                                         # 第三层,10个神经元,**没有激活函数**(默认 linear),输出           logits
])# 编译模型
model1.compile(optimizer=Adam(),                                 # 优化器使用Adamloss=SparseCategoricalCrossentropy(from_logits=True), # 损失函数用稀疏分类交叉熵,告诉它输出是logits,内部会帮你做           softmaxmetrics=['accuracy']                               # 评估指标用准确率
)
  • 优点:数值稳定性好,训练更高效,官方推荐训练方式
  • 缺点:预测时输出是 logits,需要额外手动 softmax 转成概率
卷积层

一种不同与我们之前学过的密集层dense layer

我们之前学过的密集层,后面的一个密集层种的每一个神经元都是由前一层的所有的激活值的函数
在这里插入图片描述
而卷积层:

这个神经元(每个卷积核(滤波器))只能查看这个小矩形区域种的像素
在这里插入图片描述
为什么会这样呢?上图已经表明了,

这种卷积层可以加快计算,需要更少的训练数据。

而需要更少的参数也有一个好处就是减低过拟合的风险

卷积层又可以构成卷积神经网络。

卷积神经网络的核心也是卷积层。

卷积神经网络

一个例子:
每一层中的每一个单元只查看输入的有限窗口
在这里插入图片描述

卷积神经网络是一种整体的神经网络结构,通常由多个卷积层、池化层(Pooling Layer)、全连接层(Dense Layer)等组成。

在卷积神经网络中的卷积层呈现这样的特点:

每个卷积核(滤波器)只“观察”输入的一小块区域(称为感受野)

滤波器在输入上滑动(卷积操作),提取局部特征

共享权重(卷积核参数),大大减少参数数量

保持输入的空间结构(二维/三维结构)

为什么卷积层更有效?

  • 局部连接:只关注邻域像素,捕获局部特征(边缘、纹理等)
  • 参数共享:同一个卷积核在整个图像上滑动,权重重复使用,参数少,计算更快
  • 平移不变性:卷积能识别图像中的相同特征,无论它们在图像哪个位置

神经网络中的偏差或方差

当训练一个神经网络模型时,会有以下的流程:
在这里插入图片描述
我们如果想避免在神经网络中出现偏差或方差,最好的方法是加正则化
代码表示是如下图:
在这里插入图片描述

无监督学习

无监督学习最常用的一种算法就是聚类了

聚类

聚类算法会查看多个数据点,并自动找到相关或相似的数据点
他们会像这样查看数据集并尝试查看是否可以将其分组为聚类,即彼此相似的点组,所以在这种情况下,聚类算法可能会发现这个数据集由这里展示的两个聚类的数据组成。
最常用的聚类算法:K-means算法

K-means算法

k均值算法的第一步是随机猜测簇中心的位置。簇的中心称为簇质心
这里举的例子是一个红色的一个蓝色的
在这里插入图片描述
在猜测完簇中心后,它会遍历所有这些样本,它会检查它是更接近红色簇质心,还是更接近蓝色簇质心,它会将每一个点分配到离它更近的质心
第二步是查看所有的红色点并取它们的平均值,然后它会让红叉也就是红色簇质心移动到红点的平均位置,同理蓝色也是查看所有的蓝色点并取它们的平均值,然后它会让蓝叉也就是蓝色簇质心移动到蓝点的平均位置。
第三步,我们将进一步检查所有的点,判断它是否更接近新位置的红色或蓝色簇质心,可能有些点的颜色将发生变化
然后将对其进行循环,把变换后的点计算所有红色点的平均位置,并改变红叉的位置,让它移动到新的位置,蓝色也同理
重复上述的步骤,最终会导致不能移动。点的颜色和质心都不再改变
在这里插入图片描述
以上是对这个算法的直观理解
下面是总结:
在这里插入图片描述

在这里插入图片描述
这实际上就是K-means算法的两个步骤的本质。

k-means算法的初始化

k-means聚类算法的第一步是选择随机位置作为初始猜测的聚类中心,并选择mu1到muk
一般来讲初始化的方法是多次随机初始化
常见做法是:

  1. 设定 运行次数 n_init(比如 10 次或 20 次)。
  2. 每次:
    • 随机选择 k 个初始中心;
    • 完整地运行一次 K-Means;
    • 计算最终的失真函数 。
  3. 在所有运行结果中,选择 代价函数最小的那次 作为最终结果。
选择聚类的个数

一般是用ELBOW的方法
肘部法(Elbow Method)

  • 思想:随着聚类数 kkk 增加,失真函数(SSE,平方误差和)会不断减小。
  • 但是 kkk 增大到一定程度后,SSE 的下降幅度会突然变小,形成一个“肘部”。
  • 这个拐点对应的 kkk,通常就是比较合适的聚类数。
    在这里插入图片描述
异常检测算法

异常检测(Anomaly Detection) 是无监督学习(但有时也会用半监督/监督)
异常检测指的是:在一堆正常数据中,发现那些偏离正常模式的点。比如:

  • 金融交易中的欺诈检测
  • 工业传感器的设备故障检测
  • 网络安全中的入侵检测
  • 医学图像中的病灶检测

通常异常数据占比很小(比如不到 1%),所以它跟传统分类问题不同,常常是 非平衡学习问题

密度估计技术

通常采用一种叫做密度估计的技术来进行异常检测
在这里插入图片描述
如果p(xtest)<伊普西隆那么说明异常

为了进行异常检测我们需要用到高斯分布也就是正太分布。

我们的数据大部分遵循 某种分布(最常见的是正态分布),那么:

  • 数据点落在 高概率区域 → 正常
  • 数据点落在 低概率区域 → 异常

换句话说,异常检测可以看作是 概率密度估计问题:判断一个数据点出现的概率是否足够低。

基于高斯分布的异常检测算法

1.特征选择
我们先挑选可能和异常相关的特征,比如:

  • 网络流量的请求次数
  • CPU 使用率
  • 温度传感器的数值
  • 等等
    假设共有 n 个特征,样本为:
    在这里插入图片描述
    2.拟合参数
    我们假设每一个特征都服从一维高斯分布,并相互独立。
    因此,对于第 j个特征:
    在这里插入图片描述
    3.计算概率密度
    对于新样本x=(x1,x2,…xn)
    第j个特征的概率密度函数是
    在这里插入图片描述
    由于假设特征相互独立,总概率是各个特征概率的乘积:
    在这里插入图片描述
    4.判断是否为异常
    在这里插入图片描述
    5.算法直觉
    如果某个特征的值偏离平均值太远,它的单维高斯密度就会很小。
    因为最终是概率的乘积,只要一个或几个特征特别异常,整体 p(x) 就会变得非常小,从而触发异常判断。
    这就是检测异常的方法。
    下面我们应该对异常检测算法的性能进行评估,根据实际条件来选择合适的算法。
对异常检测算法的性能进行评估

在异常检测里,我们通常面对的是未标记的数据,但如果想比较不同特征,不同参数(如ε)的好坏,就需要一个数值化的评估方法。
一旦有了少量标记的异常标签,就可以借此做“监督式”的性能度量。
那么我们可以怎么评估呢?

1.训练集+验证集+测试集

对于训练集:

假设大部分都是正常样本 y=0,即使混入少量异常,模型也能学到正常数据的分布。

对于交叉验证集:

混合了少量标记的正常样本 (y=0) 和异常样本 (y=1),用于选择超参数(如 ε 阈值)。

对于测试集:

用于最终独立评估模型性能。

  • 若没有测试集,就无法完全保证模型在未来数据上的泛化表现。

训练学参数 → 验证集调 ε → 测试集报告性能

2.不使用测试集,仅使用训练集和交叉验证集

省数据,但缺点是没有一个公平的方法来判断它在未来的例子中实际表现如何,因为没有测试集。

3.混淆矩阵

在交叉验证集/测试集上比较预测值和真实标签:

预测正常 (0)预测异常 (1)
实际正常 (0)TNFP
实际异常 (1)FNTP
  • TP (True Positive): 异常 → 被正确识别
  • FP (False Positive): 正常 → 被误判异常
  • FN (False Negative): 异常 → 被漏掉
  • TN (True Negative): 正常 → 正确识别

通过核心指标:
在这里插入图片描述
等来实现评估。

异常检测和监督式二分类的区别
对比维度异常检测(通常是无监督/半监督)监督式二分类(Supervised Classification)
数据需求主要依赖大量 正常样本 来建模分布 p(x),只需极少或不需要异常样本。需要大量 正常样本 + 异常样本,并且异常样本要有代表性。
检测逻辑假设异常是 稀有偏离正常模式,不需覆盖所有异常类型。假设未来的异常和训练集里的异常类似,若类型不全,容易漏检。
适用场景- 设备故障监测(故障极少)
- 入侵检测(攻击方式不断变化)
- 罕见病检测(病例稀缺)
- 垃圾邮件识别(大量垃圾邮件样本)
- 欺诈检测(有丰富历史欺诈数据)
方法示例高斯分布建模、混合高斯模型、孤立森林、One-class SVM、自编码器逻辑回归、决策树、SVM、神经网络等
优劣能发现 未知的新型异常;但阈值选择敏感,易误报。对已知类型效果好;但对 未见过的异常类型 敏感度低。

当异常样本 极少 且 难以覆盖所有类型 → 用 异常检测。
当异常样本 足够多 且 能代表所有类型 → 用 监督式二分类。

异常检测中的特征选择与改进方法
  • 异常检测
    • 主要依赖无标签的正常数据来建模 p(x)。
    • 没有监督信号帮助区分有效/无效特征。
    • 如果特征选择不好,模型可能无法区分正常和异常 → 导致高漏检率

因此,异常检测比监督学习更依赖于合理的特征设计
那么怎么对特征进行调整?
目标:让特征更接近高斯分布,以便高斯模型更好地拟合。
常用方法:

  • 对数变换:log⁡(x+1)
  • 平方根变换:x\sqrt{x}x
  • Box-Cox 或 Yeo-Johnson 变换

注意:训练集、验证集、测试集都要应用相同的变换

通过新增特征提升异常检测性能

有时候会遇到的最常见问题是p(x)的值相近,也就是正常和异常样本的p(x)都比较大,即使异常只有一个,算法也无法标记这个特定的样本为异常。在这种情况下,通常会查看该样本,并找出是什么让我认为这是一个异常即使特征X1的值与其他训练样本相似,如果我能识别一些新的特征比如x2就能够帮助区分这个样本和正常样本,那么添加该特征就可以帮助提高算法的性能。

  1. 训练初始模型 → 在交叉验证集上评估性能。
  2. 找出未检测到的异常样本
  3. 分析这些样本:为什么它们的 p(x)值和正常样本很接近?
  4. 提出新特征,帮助区分这些异常:
    • 例如:某异常点的 x1x_1x1 与正常样本类似,但如果增加 x2x_2x2,就能明显区分。
  5. 重新训练并评估模型性能。
异常检测中特征选择的原则
  • 尽量选择与“异常可能出现的机制”相关的特征。
  • 避免无关特征(它们会让分布估计更复杂,但没有区分度)。

总结异常检测算法的流程:

初始特征建模 → 估计 p(x)。

误差分析 → 找出漏检异常。

创造新特征 / 转换特征 → 让正常样本和异常样本更可区分。

迭代优化

强化学习

定义

强化学习:通过不断试错 + 奖励反馈 → 找到最优策略
给它一个奖励函数,让它知道什么时候做好,什么时候做不好,算法的工作就是自动找出如何选择好的行动。

终端状态

终端状态:在该状态下获得奖励,当获得奖励后就不会再有新的动作或新的状态。

强化学习最小四要素

状态,动作,奖励,和下一个状态。
这基本上是每一次你采取一个动作时发生的情况。

强化学习中如何区别哪一个奖励更好呢?
这就涉及到一个概念叫回报。

回报

在机器学习中回报是系统获得奖励的总和。
能更快得到的奖励可能比那些需要更长时间才能得到的奖励更有吸引力。
因为有一个折现因子的存在:

折现因子

折现因子,折现因子通常会是一个接近1的数。越早得到奖励,回报越高。
早点拿到的奖励比晚点的更有吸引力

策略

强化学习的目标是找到一个策略pi告诉外面在每一个状态下应该采用什麽行动。

马尔可夫决策过程

在强化学习中,大多数问题都会用马尔可夫决策过程(MDP)来建模
它的定义是一个五元组
在这里插入图片描述
S(States)状态集:环境可能的所有情况

A(Actions)动作集:智能体在状态下能做的所有动作

P(转移概率):执行动作后,从当前状态到下一个状态的概率

R(奖励函数):在状态 s 下采取动作 a 后的即时奖励

γ(折扣因子):衡量未来奖励的重要性

马尔可夫性质:未来只取决于现在,而与过去无关。。

马尔可夫是指未来只取决于当前状态,而不是取决于你到达之前可能发生的任何事情。也即未来只取决于你现在所处的位置,而不是你如何到达这里的

状态动作价值函数

开发一个选择好动作的算法,第一步是定义并最终学习计算状态动作价值函数。

这个函数通常是由大写字母Q来表示,它是你可能处于的某个状态的函数,以及你在该状态中选择采取的行动的函数。

即: Q(s,a)表示从s状态开始,并只采用一次动作a,然后会以最优方式行动的返回值(回报)

Q(s,a) 表示:如果你现在处在状态 s,立刻采取动作 a,然后之后按最优方式去行动,你期望能得到的累计回报

如果能计算每一个状态和行动的Q(s,a)那么我们就可以得到一种计算最优测试pi(s)的方法。
在这里插入图片描述
如果你能计算状态-动作值函数Q(S,A),那么它会给你一个从每一个状态选择一个好动作的方法。只要选择给你最大的Q(S,A)值的动作A。

那么怎么计算呢?有一个关键的方法叫做Bellman方程

Bellman 方程就是用来递归地计算状态值函数 V(s) 或者状态–动作值函数 Q(s,a) 的。

Bellman方程

在这里插入图片描述
R(s)的意思是即时奖励。在这里插入图片描述

随机环境

在某些应用下,当采取行动的时候,结果往往不可靠的,例如如果你命令你的火星探测器向左走,可能会有一些滑坡,使他滑倒并朝错误的方向行驶。

在实践中,很多机器人不总是能完全按照指令行事。因为风把它吹偏了。

强化学习具有普遍性,它可以建模随机或随机环境。

强化学习算法的任务就是选择一个策略pi以最大化平均值或期望的总和。

当你有一个随机强化学习问题或随机马尔可夫决策过程,目标是选择一个策略,告诉在状态s下采取什么行动A,以最大化期望回报。

因为随机环境的存在,贝尔曼方程需要做出改动。因为我们不确定下一次s‘是什么,我们需要对所有可能的下一个状态求期望
在这里插入图片描述

DQN算法

我们之前学过的,要找一个最优的Q值函数,指导机器在每一个状态下选择动作。

关键的思想是我们将训练一个神经网络来计算或近似状态-动作价值函数。(Q值函数)

这就是深度Q网络(也就是DQN算法)

该网络通过输入当前状态和当前动作,并计算或近似Q值函数

过程如下:

刚开始的参数是随机猜的,类似线性回归。

通过与环境交互,产生训练样本,把这些样本放到重放缓冲区。

从缓冲区里面随机采样一批样本,打破相关性,提高训练稳定性。

计算目标值,通过贝尔曼方程加上新的参数,来计算出新的Q值函数,

通过梯度下降来不断优化θ。使得Q网络逐渐收敛。

强化学习中仅存储最近示例的技术被称为重放缓冲区

重放缓冲区

  • 如果只用最新的样本来训练,样本之间强相关,容易导致训练不稳定。
  • 重放缓冲区:存储最近的交互数据 (s,a,r,s′),在训练时随机抽取小批量样本进行训练。
  • 优点:
    1. 打破数据相关性,提升稳定性;
    2. 提高样本利用率。

对DQN算法的优化 改进的神经网络结构

把多个动作的Q值一次性输出,而不是为每一个动作分别跑一次网络

大多数DQN的实现实际上都使用了这种更高效的架构,

普通 DQN 的做法(低效)

  • 输入:一个状态 s。(可能包括不同的维度:位置,速度,角度,角速度,有几个输入就说明有多少个维度)
  • 网络:每次输入状态 + 一个动作 a,输出对应的 Q(s,a)。
  • 如果有 4 个动作,那就要跑 4 次网络,才能比较哪个动作的 Q 值大。

改进后的 DQN(更高效)

  • 输入:只输入状态 s。(可能包括不同的维度:位置,速度,角度,角速度,有几个输入就说明有多少个维度)
  • 网络:最后一层不只输出一个值,而是直接输出 所有动作的 Q 值向量
  • 输出:例如有 4 个动作,就一次性输出 4 个 Q 值 Q(s,a1),Q(s,a2),Q(s,a3),Q(s,a4)。
  • 这样只需要推理一次,就能选择最大 Q 值的动作。
    在这里插入图片描述

对DQN算法的优化 ε-贪婪策略

学习如何逼近Q(s,a),有一种常见的方法是使用ε-贪婪策略

当算法仍然在运行的时候,我们不知道在每一个i状态下该采取的最佳行动是什么,我们知道,刚开始DQN算法的时候是随机选择输入的,但这样会是一个糟糕的行动,所以每当我们处于状态S时,选择一个可以最大化Q(s,a)的行动a,

比如在95%的概率下,采取最大化Q(s,a)的行动a,在%5的概shid率下,随机采取行动。

为什么我们偶尔还是要随机选择一个行动呢?这是因为假设由于某种奇怪的原因,Q(s,a)被随机初始化,使得学习算法认为启动主引擎不是一个好的主意,可能Q值一直很低,那么它将总是选择最大化Q(s,a)而不会尝试启动主引擎。而这种随机选择动作的方法被称为探索步骤这种选择最大化的方法就被称为贪婪策略。也被称为利用步骤

ε-是指的是随机化阶段%5,剩余是贪婪策略

一开始多使用随机化然后慢慢的取消它而是更多的用不断改进的Q函数来选择好的动作。即开始的ε会很高,会逐渐降低到甚至0.01,其他时间都使用贪婪策略。

强化学习跟监督学习相比在选择超参数的时候更加挑剔,有时候调整这些参数很麻烦。·

对DQN算法的优化 小批量和软更新

什么是小批量呢?

在训练神经网络时,不是每次都用所有样本(全量训练,太慢),
也不是只用一个样本(单样本训练,太不稳定)。而是 随机抽取一个子集 (batch) 来更新参数。

例如:即使你在重放缓冲区存储了10000个元组,可能会不选择使用全部的元组,相反可能只会选择取一个子集。比如1000个并用它来创建1000个训练样本,事实证明每次迭代训练模型时变得有些噪声,但速度更快。

为啥会有噪声呢?

每次训练时用的数据是不同的随机子集 → 每次算出来的梯度方向并不完全一样。这种随机性会让训练过程带有波动(loss 曲线抖动)。

但好处是:

  1. 提高效率(每次只处理一小部分数据)。
  2. 这种抖动反而能帮助模型跳出局部最优,更好地逼近全局最优。

作用:可以加快速度,同样也可以加快监督学习算法的速度

什么是软更新呢?

软更新实际上也是自我更新

这种方法有助于防止Qnew仅因为一步运气不佳而变遭。

比如说当前预测的值是Q(s,a)我们如果直接硬更新的话,会把这个参数全部复制给目标网络,每次更新参数 θ 后,右边的 max⁡Q(s′,a′)也跟着变了;

这样的训练目标y就会不断的跳动,模型就学不到稳定的东西
在这里插入图片描述
而软更新不会一次就把参数全部换掉,而是缓慢靠近:
在这里插入图片描述
w=1%的wnew+%99的w旧

b=1%的bnew+%99的b旧

每次只拿在线网络参数的一点点(1‰),

其余大部分(99.9%)保持旧的目标网络参数。

目标网络更新得很平滑,不会突然大幅度跳变;

所以计算出来的目标值 y也很稳定;

学习曲线就更平滑,更容易收敛。

作用:可以使强化学习算法能更好的收敛到一个好的解决方案

强化学习的状态

强化学习的应用远少于监督学习和无监督学习

表现基准

表现基准:
一个参考的最低错误率,比如人类水平、现有算法表现、或者随机猜测的错误率。用来判断模型性能是否有优化空间。
在这里插入图片描述

模型评估

如果我们已经训练了一个机器学习模型,如何评估该模型的性能。
需要一个系统的方法来评估性能。
在多于一个或者两个以上的特征的应用中绘制图像是很困难的,那我们怎么去评估模型的表现呢?
我们可以把数据集分为两部分,70%的是作为训练集,30%是作为测试集,我们在测试集上测试性能。或者是80%and20%
因此训练机器学习模型(特别是线性模型或神经网络)的一般流程,包括:

  1. 最小化代价函数(cost function)来训练参数
  2. 计算训练误差(training error)
  3. 计算测试误差(test error)
    在这里插入图片描述
    我们发现只有在最小化代价函数中加了正则化,而没有在计算训练误差和测试误差的时候出现,这是因为正则化项只加在训练时的目标函数中,不影响训练误差和测试误差的评估过程。

泛化能力(模型评估的一个指标)

所谓泛化能力就是你训练出来的模型,能否处理从来没有见过的新数据。
在这里插入图片描述
在这里插入图片描述
训练集上预测错误的比例可以衡量是否欠拟合,
测试集上预测错误的比例可以衡量泛化能力,是否过拟合。

倾斜数据集的误差指标

如果在开发一个机器学习应用程序时,正反例的比例失衡,远非50-50

那么通常的错误度量标准如准确率就不会那么高效

在处理数据集不平衡的问题时,我们通常使用一共不同的错误指标,而不是仅仅分类错误来判断学习效果

这时提供了一对常见的错误指标就是精确率和召回率

换句话说:

在分类问题中,如果正反例比例严重失衡(例如欺诈检测、癌症筛查等),传统的**准确率(Accuracy)**就失去了参考意义。
比如,在癌症检测中,如果 99% 的样本都是健康的,分类器即使永远预测“健康”,也能得到 99% 的准确率,但这毫无价值。

因此,我们需要更有意义的评价指标——精确率(Precision)\和\召回率(Recall)

这里要用到混淆矩阵来引出精确率和召回率

混淆矩阵是一种常用工具,用于对比模型的预测结果和真实标签。
在这里插入图片描述
TP(真正例):预测为正,实际也为正

FP(假正例):预测为正,实际为负

FN(假负例):预测为负,实际为正

TN(真负例):预测为负,实际也为负

根据混淆矩阵的内容,似乎会来计算两个指标?

精确度和召回率

精确度定义为真正例的数量除以被分类为正例的数量,

召回率被定义为真正例的数量除以实际正例的数量
在这里插入图片描述
召回率这个术语来源于这样的观察,如果你有一群患者或者一个患者群体,

召回率衡量的是在所有患病的患者中你能准确的诊断出有多少患者真正患病

因此,当有一共偏斜类或者一个稀有类别需要检测时,精准度和召回率很有用

精确率与召回率之间的权衡

在分类任务中,**精确率(Precision)召回率(Recall)**往往是矛盾的:

  • 如果我们提高分类阈值,模型会更严格地判定“正类”,这会提高精确率(预测为正的更可靠),但会漏掉一些真正的正例,召回率降低。
  • 如果我们降低分类阈值,模型更容易预测为“正类”,召回率提高(更多正例被识别出来),但同时会引入更多假正例,精确率下降。

因此,如何在两者之间找到平衡点非常重要。

手动选择阈值以权衡精准率和召回率将是最终要做的事情

如果你不想手动去做,那么提出了一共叫做F1分数的指标,它有时被用来自动结合精准率和召回率

当用不同的算法得出精准率和召回率的时候,哪一个算法的F1 score的值越大,选择哪一种算法。
在这里插入图片描述
F1 score实际上是算精确率和召回率的调和平均数

模型选择

我们之前用
练集
:用来训练模型测试集:用来评估模型性能,
乍一看很合理,但实际上有很大隐患:

  • 训练时调参数(比如模型复杂度、正则化系数、训练轮数)时,往往需要“试错”
  • 如果用测试集来试错,一旦你多次调整模型参数去让测试集误差最低,测试集就不再是“独立的未见数据”了
  • 这会导致“测试集泄露”,评估结果失真,模型对测试集过拟合
    因此,引入验证集,把数据分为三部分训练集,交叉验证集(验证集,开发集),测试集。
    训练集相当于练习题,交叉验证集相当于模拟考试通过模拟考试选出得分高的,测试集相当于高考
    训练集是用于训练,交叉验证集是用于选出最好的,测试集是用于测试
    交叉验证的意思是指我们将使用这个额外的数据集来交叉检查有效性或者不同模型的准确性。每一次“训练”和“验证”的数据集位置都在“交叉”变换

有了这三个集我们就可以计算训练误差,,测试误差。这些都不包含正则化,正则化项只包含在训练目标中。
在这里插入图片描述
在这里插入图片描述
我们在一系列不同复杂度的线性回归模型中,利用验证集选择了泛化误差最小的模型。最终,再用测试集来对所选模型的泛化能力进行独立评估。

误差分析

在机器学习里,**误差分析(Error Analysis)**是指:通过分析模型预测结果和真实结果之间的差异,找出误差的组成和来源,从而判断模型性能问题的根本原因,并指导优化方向。
它不是随便看看错了多少,而是系统地回答三个问题:

  1. 模型的误差主要来自哪一部分?(偏差?方差?噪声?)
  2. 这些误差分别占多大比例?
  3. 下一步该从哪里下手优化才能最有效?

模型优化

添加数据
数据增强

在图像识别中,常常采用一种方法来添加数据—数据增强:
在这里插入图片描述
语音识别中也可以用:
在这里插入图片描述

数据合成

数据合成往往用于计算机视觉。

从0开始编造全新的样本,而不是修改现成的样本。

以照片OCR(照片光学字符识别)为例,识别照片中的光学字符并用计算机自动读取其中出现的文本。

我们可以识别一个图片中的文本:
在这里插入图片描述
那么相同的道理,我们是不是也可以用数据合成的方式,创造一个类似的数据:
在这里插入图片描述
那么我们就不仅仅可以用真实的数据来训练模型,也可以用数据合成的方法来生成数据来训练模型。
传统的机器学习训练模型的方式往往是下载数据集并固定数据的同时,专注于
改进代码算法或模型。
在这里插入图片描述
那么我们也可以注重数据驱动的方法,反而更有成效。通过对数据的处理,使得模型的输入质量更高,从而提升性能。

在数据量有限的任务中,花时间在数据质量和多样性上的提升,往往比单纯调模型结构更有效。

在这里插入图片描述

机器学习的迭代发展

一个合适的机器学习模型的产生过程:

从选择结构开始,不断循环。
在这里插入图片描述

完整的机器学习项目周期

以语音识别作为例子:

1.先确定项目的范围,即你要做什么

2.收集数据,要决定需要什么数据来训练我们的机器学习系统。得到数据集

3.训练模型,进行误差分析/偏差-方差分析,并迭代改进模型。

4.根据分析来改进,例如我们可能要回去收集更多的数据,可鞥是所有类型的数据,也可能只是收集更多错误分析中指出的特定类型的数据。比如可能使用数据增强。
进行周期型循环

5.当觉得训练的差不多的时候,就可以部署到生产环境中供用户使用,当部署一个系统时,你还需要保持持续监控系统的性能,以便在性能下降的时候进行维护,而不是仅仅的只托管在服务器端。用户在使用模型的时候也能让我们获得更多的数据,从而进一步提升性能。

例如用户在使用一个移动app的语音识别功能时,使用时就是点意一个api,这个api会将录制的音频片段传递到推理服务器上,推理服务器的任务是应用机器学习模型,然后返回模型的预测结果

机器学习的应用

推荐系统

推荐系统的商业影响力和实际应用案例的数量远超它在学术界所获得的关注

在典型的推荐系统中,你会有一定数量的用户以及一定数量的项目,在这个例子中,项目是你想推荐给用户的电影。当然同样的逻辑或者框架可以用于推荐任何网站可能销售的产品。

如果我们有每一个项目的特征,添加特征的思想
在这里插入图片描述
推荐系统一般是用线性回归,但它是有一定限制的,因为有些时候一些数据是残缺的。

我们之前是直接得出了x1,x2 告诉我们这是一部浪漫电影的程度,动作电影的程度。

那么我们就可以使用线性回归来学习预测电影评分,但如果我们不知道x1和x2,那么该怎么办呢?实际上我们可以从数据中学习或得出这些特征,

我们只有 用户-电影评分矩阵(谁给谁打了几分)。我们没法靠人工定义好特征,所以 我们希望能自动从数据里学习到这些“隐藏特征”。

从而引出协同过滤算法

协同过滤算法

对于大多数的机器学习算法,特征必须外部提供,但在这个算法,可以学习得到特征。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们把只学用户参数w和只学电影参数x的式子合并,进行协同过滤,这里所有用户参数 w(u)所有物品参数 x(i)都是变量。
那怎么求呢?
因为:

  • 这个函数 关于所有参数是连续可导的
  • 我们可以用 梯度下降(Gradient Descent)
    在这里插入图片描述
    推荐系统或协同过滤算法的许多重要应用都涉及二元标签,用户告诉你喜欢不不喜欢,那么我们如何将算法推到这种情况呢,就像从线性回归推到逻辑回归
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
均值归一化

为什么要做均值归一化?

在推荐系统里,我们预测的是用户对电影的评分 y(u,i),但不同用户的评分习惯可能差别很大:

  • 用户 A 平均给分 4 星,多数电影都给高分。
  • 用户 B 平均给分 2 星,多数电影都给低分。

如果直接把原始评分矩阵拿去训练模型,算法会很难同时拟合这些差异大的数据,梯度下降收敛慢、容易陷入局部最优

在线性回归中特征归一化可以帮助算法运行得更快,在构建一个算法广泛的推荐系统时,先进行均值归一化会使得运行得更高效,性能有所提升。
在这里插入图片描述
学习模型的代价函数为:
在这里插入图片描述
然后用均值归一化可以让算法更快,结果更好。

使用TensorFlow来实现协同过滤

对于许多应用,为了实现梯度下降,需要找到成本函数的导数。
TensorFlow可以自动帮你找到成本函数的导数。
在这里插入图片描述
这又被称为autodiff

import tensorflow as tfw = tf.Variable(3.0)  # 参数
x = 2.0
y = 1.0  # 目标值
alpha = 0.01for i in range(30):with tf.GradientTape() as tape:fwb = w * xcost = (fwb - y)**2# 自动求导dJdw = tape.gradient(cost, [w])# 梯度下降更新w.assign_sub(alpha * dJdw[0])

也可以使用 TensorFlow + Keras 的优化器(Adam) 实现协同过滤

# 初始化优化器
optimizer = keras.optimizers.Adam(learning_rate=1e-1)
# Adam 是一种自适应梯度下降算法,会自动调整每个参数的学习率
# 迭代训练
iterations = 200
for iter in range(iterations):
# 迭代 200 次,让参数收敛到最优值
# 前向计算成本函数 + 自动求导
with tf.GradientTape() as tape:cost_value = cofiCostFuncV(x, w, b, Ynorm, R, num_users, num_movies, lambda)
#cofiCostFuncV 是你定义的协同过滤代价函数
#包括用户向量 w、物品向量 x、偏置 b
#以及评分矩阵 Ynorm、指示矩阵 R 等
#GradientTape 会记录前向计算图,为后续自动求梯度做准备
# 自动求梯度
grads = tape.gradient(cost_value, [x, w, b])
# TensorFlow 会计算代价函数对所有参数的梯度 [∂J/∂x, ∂J/∂w, ∂J/∂b]
# 不需要手动推导偏导数
# 用优化器更新参数
optimizer.apply_gradients(zip(grads, [x, w, b]))
# zip(grads, [x, w, b]) 将梯度和对应参数配对
# Adam 优化器会根据梯度更新每个参数,使成本函数下降

这是 TensorFlow 中训练协同过滤模型的标准流程:前向计算 → 自动求导 → 梯度下降更新参数 → 多次迭代收敛。
只需要定义代价函数和参数,TensorFlow 会自动帮你算梯度,Adam 会帮你更新参数。

协同过滤里隐特征的相似性搜索

1.隐特征难以解释

在协同过滤里,物品特征 x(i)x^{(i)}x(i) 是算法自动学习得到的 隐特征(latent features)

这些特征不像你手动定义的“浪漫程度、动作程度”等显性特征,可直接理解

所以单个特征的意义不容易解释

2.利用隐特征找相似物品
在这里插入图片描述
为什么这样有效

  • 协同过滤学习的隐特征向量 编码了用户评分模式
  • 如果两个物品的隐特征向量很相似,说明用户对它们的评分模式也相似
  • 即便你看不懂每个维度代表什么,但算法保证了向量相似 → 用户喜好相似
冷启动问题

新物品冷启动

  • 新上线的物品(如新电影、新商品)几乎没有用户评分
  • 协同过滤依赖历史评分来学习隐特征,所以对新物品无法预测评分
  • 结果就是:新物品很难被推荐

新用户冷启动

  • 新用户刚注册,只给出了很少的评分或点击记录
  • 系统对这个用户的喜好了解不足
  • 无法给出精准个性化推荐

在这些情况下,协同过滤是不足的,对于新的物品,我们可以利用物品侧信息(类型、演员、制作方等)做内容推荐,对于新用户,我们可以利用用户侧信息(人口统计、兴趣标签)做初步推荐。

实务中常用 混合推荐系统(Hybrid Recommendation),同时使用协同过滤和基于内容的过滤

基于内容的过滤
  • 核心思想:利用用户的特征 & 物品的特征,来判断匹配度。
    • 物品特征(电影 → 类型、演员、导演、关键词)
    • 用户特征(人口统计信息、兴趣标签、历史偏好)
  • 推荐方法
    1. 建立用户画像(profile),例如“这个用户偏好动作片+科幻片”
    2. 对候选物品进行特征建模
    3. 计算用户画像与物品特征之间的相似度(如余弦相似度、点积、分类模型)
    4. 推荐相似度最高的物品
      使用深度学习是一种很好的方法来开发基于内容的过滤算法
      用户网络:
      在这里插入图片描述
      电影网络:
      在这里插入图片描述
      在这里插入图片描述
      代价函数尽可能的小
      每部电影都会通过「电影网络」学到一个向量表示
      在这里插入图片描述
      距离越小,电影越相似。
从大目录中推荐

如今推荐系统有时需要从成千上万的商品目录中挑选几件来推荐。

如果需要通过这个神经网络处理成千上万甚至数百万个项目以计算内积来确定你应该推荐哪些产品,那么每次用户访问你的网站时都要运行成千上万次或数百万次的神经网络推理,这很明显是不可行的。

于是许多大型项目都是分两步走的:1.检索2.排序

检索阶段:快速从海量物品中找出少量(例如 100、500、1000)候选项,保证高召回(recall)。

排序阶段:在候选上跑高精度模型(深度、特征丰富),输出最终 Top-K(precision)

检索

目标:快速生成一个候选池(100~1000个物品),覆盖用户可能感兴趣的内容。
特点:速度要快、覆盖要广,哪怕候选里有很多噪音也没关系。

这一阶段的重点是 覆盖率(Recall):确保真正相关的物品不会被漏掉。

这个想法是在检索步骤中,我们生成一个包含大量可能推荐项目候选选项的列表,以覆盖许多可能推荐的内容,给用户,在检索步骤中即使包含大量用户不太可能喜欢的项目也是可以的,然后在排序步骤中,我们会微调并挑选最好的项目推荐给用户,这里有一个例子:在检索步骤中,我们可能会对用户最近观看的10部电影中的每一部找到最相似的10部电影,如果用户看过电影i,其向量为vim,可能会找到相似的向量为vkm的电影。求它们之间的距离差的平方

排序

排序阶段(Ranking)

目标:从候选池里精挑细选,输出最终 Top-K(精确推荐列表)
特点:模型更复杂、更精细,注重 相关性和个性化

接着会排序,获取检索步骤中的列表,利用训练好的模型进行排序,这意味着你需要将用户特征向量和电影特征向量输入到这个神经网络中,对于每一个用户-电影组合计算出预测评分,然后根据这些评分,向用户展示排名列表。

还有一个优化是如果你已经预先计算出所有电影的特征向量vm,那么需要在神经网络的这个部分上进行一次推理计算以得到vu,然后把vm和vu进行内积
在这里插入图片描述
需要明确的是在检索过程中你想要检索多少项目,然后将这些项目纳入更精确的排名步骤。
在检索步骤中,检索更多的项目是好的,但是会使算法变慢。
增加项目数量对提高相关性有作用,特别是神经网络模型yij等于1的概率估计值,或者如果你的模型预测检索到的项目的y的估计评分较高,最终会高的多。

基于内容过滤的TensorFlow的实现

在这里插入图片描述

总结

一个优秀的机器学习工程师/研究者,做的工作其实就是:

  • 理解问题本质(选合适的模型和损失函数)
  • 有经验地调超参数(比如调学习率、dropout、防止过拟合)
  • 根据验证集结果反复试验(模型越大,试验越多)

文章转载自:

http://ep4mx127.hxLch.cn
http://LLjJ2C38.hxLch.cn
http://2VJDvl49.hxLch.cn
http://vISgLtkc.hxLch.cn
http://UtDBnvSk.hxLch.cn
http://HgRTuIvJ.hxLch.cn
http://42E9cuCA.hxLch.cn
http://qYxIiMNL.hxLch.cn
http://PsiTZQXX.hxLch.cn
http://a9H7LLOr.hxLch.cn
http://e7Wej72Z.hxLch.cn
http://8mfeWQGE.hxLch.cn
http://uHh5xbDd.hxLch.cn
http://kMj1lC9O.hxLch.cn
http://gQhmoWBh.hxLch.cn
http://3L5DQBws.hxLch.cn
http://ivOQZVxN.hxLch.cn
http://uczHbG4e.hxLch.cn
http://2oFiGRIh.hxLch.cn
http://Yiu8gCSc.hxLch.cn
http://4Fq9QWZd.hxLch.cn
http://us7tvIrz.hxLch.cn
http://vNVogEfC.hxLch.cn
http://8fzIeM3v.hxLch.cn
http://lx8ATHtq.hxLch.cn
http://ZdX9shbj.hxLch.cn
http://S1mgjO41.hxLch.cn
http://VzNLq9ED.hxLch.cn
http://ieLn09OL.hxLch.cn
http://TVTkpLZT.hxLch.cn
http://www.dtcms.com/a/383249.html

相关文章:

  • Uniswap:DeFi领域的革命性交易协议
  • 3. 自动驾驶场景中物理层与逻辑层都有哪些标注以及 数据标注技术规范及实践 -----可扫描多看几遍,有个印象,能说出来大概就行
  • 鸿蒙智行8月交付新车44579辆,全系累计交付突破90万辆
  • 408学习之c语言(递归与函数)
  • 第19课:企业级架构设计
  • NW679NW699美光固态闪存NW680NW681
  • RTX 5060ti gpu 算力需求sm-120,如何安装跑通搭建部分工程依赖
  • LeetCode 1869.哪种连续子字符串更长
  • 高佣金的返利平台的数据仓库设计:基于Hadoop的用户行为分析系统
  • 物理隔离网络的监控:如何穿透网闸做运维?
  • 知识图谱网页版可视化可移动代码
  • 【iOS】static、const、extern关键字
  • Grafana+Loki+Alloy构建企业级日志平台
  • Redis 实现分布式锁的探索与实践
  • 设计模式-适配器模式详解
  • Java 分布式缓存实现:结合 RMI 与本地文件缓存
  • Ajax-day2(图书管理)-渲染列表
  • 在Excel和WPS表格中快速复制上一行内容
  • 11-复习java程序设计中学习的面向对象编程
  • 《云计算如何驱动企业数字化转型:关键技术与实践案例》
  • LSTM 深度解析:从门控机制到实际应用
  • FPGA学习篇——Verilog学习Led灯的实现
  • 【ARDUINO】Arduino Uno 获取 OV7576 数据并通过 ESP8266 发送到 TCP 客户端(待测试)
  • xtuoj 原根
  • JVM 核心知识全解析:从类加载到垃圾回收的深度认知
  • Cesium4--地形(OSGB到3DTiles)
  • NLP:Transformer之self-attention(特别分享3)
  • 07 常用损失函数
  • UDP Socket 进阶:从 Echo 到字典服务器,学会 “解耦” 网络与业务
  • 多语言编码Agent解决方案(4)-Eclipse插件实现