PowerLZY's Blog

本博客主要用于记录个人学习笔记(测试阶段)

HOW 自动部署开源AI模型到生产环境?

一、背景介绍

AI的广泛应用是由AI在开源技术的进步推动的,利用功能强大的开源模型库,数据科学家们可以很容易的训练一个性能不错的模型。但是因为模型生产环境和开发环境的不同,涉及到不同角色人员:模型训练是数据科学家和数据分析师的工作,但是模型部署是开发和运维工程师的事情,导致模型上线部署却不是那么容易。

DaaS(Deployment-as-a-Service)是AutoDeployAI公司推出的基于Kubernetes的AI模型自动部署系统,提供一键式自动部署开源AI模型生成REST API,以方便在生产环境中调用。下面,我们主要演示在DaaS中如何部署经典机器学习模型,包括Scikit-learn、XGBoost、LightGBM、和PySpark ML Pipelines。关于深度学习模型的部署,会在下一章中介绍。

DMatrix 格式 在xgboost当中运行速度更快,性能更好。

二、部署准备

我们使用DaaS提供的Python客户端(DaaS-Client)来部署模型,对于XGBoost和LightGBM,我们同样使用它们的Python API来作模型训练。在训练和部署模型之前,我们需要完成以下操作。

  • 安装Python DaaS-Client
1
pip install --upgrade git+https://github.com/autodeployai/daas-client.git
1
2
3
from daas_client import DaasClient

client = DaasClient('https://192.168.64.3:30931', 'username', 'password')
  • 创建项目。DaaS使用项目管理用户不同的分析任务,一个项目中可以包含用户的各种分析资产:模型、部署、程序脚本、数据、数据源等。项目创建成功后,设置为当前活动项目,发布的模型和创建的部署都会存储在该项目下。create_project函数接受三个参数:
    • 项目名称:可以是任意有效的Linux文件目录名。
    • 项目路由:使用在部署的REST URL中来唯一表示当前项目,只能是小写英文字符(a-z),数字(0-9)和中横线-,并且-不能在开头和结尾处。
    • 项目说明(可选):可以是任意字符。
1
2
3
4
project = '部署测试'
if not client.project_exists(project):
client.create_project(project, 'deployment-test', '部署测试项目')
client.set_project(project)
  • 初始化数据。我们使用流行的分类数据集iris来训练不同的模型,并且把数据分割为训练数据集和测试数据集以方便后续使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn import datasets
from sklearn.model_selection import train_test_split
import pandas as pd

seed = 123456

iris = datasets.load_iris()
iris_target_name = 'Species'
iris_feature_names = iris.feature_names
iris_df = pd.DataFrame(iris.data, columns=iris_feature_names)
iris_df[iris_target_name] = iris.target

X, y = iris_df[iris_feature_names], iris_df[iris_target_name]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=seed)
  • 模型部署流程。主要包含以下几步
    • 训练模型。使用模型库提供的API,在iris数据集上训练模型。
    • 发布模型。调用publish函数发布模型到DaaS系统。
    • 测试模型(可选)。调用test函数获取测试API信息,可以使用任意的REST客户端程序测试模型在DaaS中是否工作正常,使用的是DaaS系统模型测试API。第一次执行test会比较慢,因为DaaS系统需要启动测试运行时环境。
    • 部署模型。发布成功后,调用deploy函数部署部署模型。可以使用任意的REST客户端程序测试模型部署,使用的是DaaS系统正式部署API。

三、部署Scikit-learn模型

  • 训练一个Scikit-learn分类模型:SVC
1
2
3
4
from sklearn.svm import SVC

model = SVC(probability=True, random_state=seed)
model.fit(X_train, y_train)
  • 发布Scikit-learn模型
1
2
3
4
5
6
publish_resp = client.publish(model,
name='iris',
mining_function='classification',
X_test=X_test,
y_test=y_test,
description='A SVC model')

test函数必须要指定前两个参数,第一个model是训练的模型对象,第二个是模型名称,其余是可选参数:

  • mining_function:指定挖掘功能,可以指定为regression(回归)、classification(分类)、和clustering(聚类)。
  • X_test和y_test:指定测试训练集,发布时计算模型评估指标,比如针对分类模型,计算正确率(Accuracy),对于回归模型,计算可释方差(explained Variance)。
  • data_test: 同样是指定测试训练集,但是该参数用在Spark模型上,非Spark模型通过X_testy_test指定。
  • description:模型描述。
  • params:记录模型参数设置。

publish_resp是一个字典类型的结果,记录了模型名称,和发布的模型版本。该模型是iris模型的第一个版本。

1
{'model_name': 'iris', 'model_version': '1'}
  • 测试Scikit-learn模型
1
2
test_resp = client.test(publish_resp['model_name'],
model_version=publish_resp['model_version'])

test_resp是一个字典类型的结果,记录了测试REST API信息。如下,其中access_token是访问令牌,一个长字符串,这里没有显示出来。endpoint_url指定测试REST API地址,payload提供了测试当前模型需要输入的请求正文格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
'access_token': 'A-LONG-STRING-OF-BEARER-TOKEN-USED-IN-HTTP-HEADER-AUTHORIZATION',
'endpoint_url': 'https://192.168.64.3:30931/api/v1/test/deployment-test/daas-python37-faas/test',
'payload': {
'args': {
'X': [{ 'petal length (cm)': 1.5,
'petal width (cm)': 0.4,
'sepal length (cm)': 5.7,
'sepal width (cm)': 4.4
}],
'model_name': 'iris',
'model_version': '1'}
}
}

使用requests调用测试API,这里我们直接使用test_resp返回的测试payload,您也可以使用自定义的数据X,但是参数model_namemodel_version必须使用上面输出的值。

1
2
3
4
5
response = requests.post(test_resp['endpoint_url'],
headers={
'Authorization': 'Bearer {token}'.format(token=test_resp['access_token'])},
json=test_resp['payload'],
verify=False)

返回结果,不同于正式部署API,除了预测结果,测试API会同时返回标准控制台输出和标准错误输出内容,以方便用户碰到错误时,查看相关信息。

1
2
3
4
5
6
7
8
9
10
# response.json()
{
'result': [{
'PredictedValue': 0,
'Probabilities': [0.8977133931668801,
0.05476023239878367,
0.047526374434336216]}],
'stderr': [],
'stdout': []
}

四、部署PySpark模型

训练一个PySpark分类模型:RandomForestClassifier。PySpark模型必须是一个PipelineModel,也就是说必须使用Pipeline来建立模型,哪怕只有一个Pipeline节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pyspark.sql import SparkSession
from pyspark.ml.classification import RandomForestClassifier
from pyspark.ml.feature import VectorAssembler
from pyspark.ml import Pipeline

spark = SparkSession.builder.getOrCreate()
df = spark.createDataFrame(iris_df)

df_train, df_test = df.randomSplit([0.7, 0.3], seed=seed)
assembler = VectorAssembler(inputCols=iris_feature_names,
outputCol='features')

rf = RandomForestClassifier(seed=seed).setLabelCol(iris_target_name)
pipe = Pipeline(stages=[assembler, rf])
model = pipe.fit(df_train)

发布PySpark模型

1
2
3
4
5
publish_resp = client.publish(model,
name='iris',
mining_function='classification',
data_test=df_test,
description='A RandomForestClassifier of Spark model')

五、模型部署管理

打开浏览器,登陆DaaS管理系统。进入项目部署测试,切换到模型标签页,有一个iris模型,最新版本是v4,类型是Spark即我们最后发布的模型。

Daas-models

点击模型,进入模型主页(概述)。当前v4是一个Spark Pipeline模型,正确率是94.23%,并且显示了iris不同版本正确率历史图。下面罗列了模型的输入和输出变量,以及评估结果,当前为空,因为还没有在DaaS中执行任何的模型评估任务。

Daas-model-overview-v4

点击v4,可以自由切换到其他版本。比如,切换到v1

DaaS-model-versions

v1版本是一个Scikit-learn SVM分类模型,正确率是98.00%。其他信息与v4类似。

DaaS-model-overview-v1

切换到模型部署标签页,有一个我们刚才创建的部署iris-svc,鼠标移动到操作菜单,选择修改设置。可以看到,当前部署服务关联的是模型v1,就是我们刚才通过deploy函数部署的iris第一个版本Scikit-learn模型。选择最新的v4,点击命令保存并且重新部署,该部署就会切换到v4版本。

参考文献

  • DaaS-Client、Sklearn、XGBoost、LightGBM、和PySpark_关注 AI/ML 模型上线、模型部署-程序员宅基地_
  • DaaS-Client:https://github.com/autodeployai/daas-client
  • 3万字长文 PySpark入门级学习教程,框架思维:https://zhuanlan.zhihu.com/p/395431025

异常检测 (anomaly detection)

异常检测工具

  • PyOD:超过30种算法,从经典模型到深度学习模型一应俱全,和sklearn的用法一致

  • Scikit-Learn:包含了4种常见的算法,简单易用

  • TODS:与PyOD类似,包含多种时间序列上的异常检测算法

异常检测算法

  • 线性模型:PCA
  • 基于相似度度量的算法:KNN、LOF、HBOS
  • 基于概率的算法:COPOD
  • 集成检测:孤立森林,XGBOD
  • 神经网络算法:自编码器

评估方法

  • ROC-AUC 曲线
  • Precision Topk:top K的准确率
  • AVE Precision:平均准确率

一、概述

1.1 什么是异常检测?

不同于常规模式下的问题和任务,异常检测针对的是少数、不可预测或不确定、罕见的事件,它具有独特的复杂性,使得一般的机器学习和深度学习技术无效。

1.2 异常检测面临的挑战

  • 未知性:异常与许多未知因素有关,例如,具有未知的突发行为、数据结构和分布的实例。它们直到真正发生时才为人所知,比如恐怖袭击、诈骗和网络入侵等应用;
  • 异常类的异构性: 异常是不规则的,一类异常可能表现出与另一类异常完全不同的异常特征。例如,在视频监控中,抢劫、交通事故和盗窃等异常事件在视觉上有很大差异;
  • 类别不均衡:异常通常是罕见的数据实例,而正常实例通常占数据的绝大部分。因此,收集大量标了标签的异常实例是困难的,甚至是不可能的。这导致在大多数应用程序中无法获得大规模的标记数据。

1.3 异常的种类:

  • 点异常(point anomalies)指的是少数个体实例是异常的,大多数个体实例是正常的,例如正常人与病人的健康指标;
  • 条件异常(conditional anomalies),又称上下文异常,指的是在特定情境下个体实例是异常的,在其他情境下都是正常的,例如在特定时间下的温度突然上升或下降,在特定场景中的快速信用卡交易;
  • 群体异常(group anomalies)指的是在群体集合中的个体实例出现异常的情况,而该个体实例自身可能不是异常,例如社交网络中虚假账号形成的集合作为群体异常子集,但子集中的个体节点可能与真实账号一样正常。

1.4 异常检测数据集:

  • 统计型数据static data(文本、网络流)
  • 序列型数据sequential data(sensor data )
  • 空间型数据spatial data(图像、视频)

1.5 异常检测的应用领域

  • 入侵检测(Intrusion detection):通过从计算机网络或计算机系统中的若干关键点收集信息并对其执行分析,从中发觉网络或系统中能不能有违反安全策略的行为和遭到袭击的迹象,并对此做出适当反应的流程。最普遍的两种入侵检测系统包括基于主机的入侵检测系统(HIDS)网络入侵检测系统(NIDS)
  • 故障检测(Fraud detection):主要是监控系统,在故障发生时可以识别,并且准确指出故障的种类以及出现位置。主要应用领域包括银行欺诈、移动蜂窝网络故障、保险欺诈、医疗欺诈。
  • 恶意软件检测(Malware Detection)
  • 医疗异常检测(Medical Anomaly Detection):通过X光片、核磁共振、CT等医学图像检测疾病或量化异常,也可以通过EEG、ECG等时序信号进行疾病检测或异常预警。
  • 深度学习用于社交网络中的异常检测(Deep learning for Anomaly detection in Social Networks)
  • 日志异常检测(Log Anomaly Detection)
  • 物联网大数据异常检测(Internet of things (IoT) Big Data Anomaly Detection):通过监控数据流信息检测异常设备和系统行为。
  • 工业异常检测(Industrial Anomalies Detection)
  • 时间序列中的异常检测(Anomaly Detection in TimeSeries)
  • 视频监控(Video Surveillance):检测视频中的异常场景。

1.6 基于标签的可获得性划分异常检测:

  • 有监督异常检测:在训练集中的正常实例和异常实例都有标签,这类方法的缺点在于数据标签难以获得或数据不均衡(正常样本数量远大于异常样本数量)。
  • 半监督异常检测在训练集中只有单一类别(正常实例)的实例,没有异常实例参与训练,目前很多异常检测研究都集中在半监督方法上,有很多声称是无监督异常检测方法的研究其实也是半监督的,对其解释的是该异常检测是无监督异常检测,学习特征的方式是无监督的,但是评价方式使用了半监督的方法,因此对于无监督与半监督的界定感觉没有那么规范。
  • 无监督异常检测:在训练集中既有正常实例也可能存在异常实例,但假设数据的比例是正常实例远大于异常实例,模型训练过程中没有标签进行校正。
  • 弱监督异常检测:该类我研究的少,不是特别了解,主要是针对异常实例不完全、粗粒度标签、部分实例标签错误等情况进行算法设计。

1.7 基于传统方法的异常检测模型

  • 基于重构的方法 假设异常点是不可被压缩的或不能从低维映射空间有效地被重构的。常见的方法有PCARobust PCA、random projection等降维方法 [4,5] 。
  • 聚类分析方法:通过聚类可以创建数据的模型,而异常点的存在可以扭曲、破坏该模型。常见的方法有Gaussian Mixture Models、 k-means、 multivariate Gaussian Models [6,7,8]。
  • 一类分类方法:对正常数据建立区分性边界,异常点被划分到边界外。常见的方法有OC-SVM [9,10]。

二、常见异常检测算法

一般情况下, 可以把异常检测看成是数据不平衡下的分类问题。因此, 如果数据条件允许, 优先使 用有监督的异常检测[6]。实验结果 [4]发现直接用XGBOOST进行有监督异常检测往往也能得到不错 的结果,没有思路时不妨一试。

而在仅有少量标签的情况下, 也可采用半监督异常检测模型。比如把无监督学习作为一种特征抽取方式来辅助监督学习 \([4,8]\), 和stacking比较类似。这种方法也可以理解成通过无监督的特征工程 对数据进行预处理后, 喂给有监督的分类模型。

但在现实情况中, 异常检测问题往往是没有标签的, 训练数据中并末标出哪些是异常点, 因此必须用无监督学习。从实用角度出发, 我们把文章的重点放在无监督学习上。

本文结构如下: 1. 介绍常见的无监督异常算法及实现; 2. 对比多种算法的检测能力;3. 对比多种算法的运算开销;4. 总结并归纳如何处理异常检测问题。5. 代码重现步骤。

2.1 无监督异常检测

如果归类的话, 无监督异常检测模型可以大致分为:

  • 统计与概率模型 (statistical and probabilistic and models) :主要是对数据的分布做出假设, 并找出假设下所定义的“异常”, 因此往往会使用极值分析Q或者假设检验。比如对最简单的一维 数据假设高斯分布 \(Q\), 然后将距离均值特定范围以外的数据当做异常点。而推广到高维后, 可以 假设每个维度各自独立, 并将各个维度上的异常度相加。如果考虑特征间的相关性, 也可以用马 氏距离 (mahalanobis distance) 来衡量数据的异常度[12]。不难看出, 这类方法最大的好处就 是速度一般比较快, 但因为存在比较强的"假设", 效果不一定很好。稍微引申一点的话, 其实给每个维度做个直方图做密度估计, 再加起来就是HBOS。
  • 线性模型(linear models):假设数据在低维空间上有嵌入, 那么无法、或者在低维空间投射后 表现不好的数据可以认为是离群点 \(Q\)
    • 举个简单的例子, PCA可以用于做异常检测 [10], 一种方法 就是找到 \(k\) 个特征向量 (eigenvectora), 并计算每个样本再经过这 \(k\) 个特征向量投射后的重建误差 (reconstruction error), 而正常点的重建误差应该小于异常点。
    • 同理, 也可以计算每个样本 到这 \(k\) 个选特征向量所构成的超空间的加权欧氏距离(特征值越小权重越大)。在相似的思路下, 我们也可以直接对协方差矩阵 \(Q\) 进行分析, 并把样本的马氏距离 (在考虑特征间关系时样本到分 布中心的距离) 作为样本的异常度, 而这种方法也可以被理解为一种软性 (Soft PCA) [6]。
    • 同时, 另一种经典算法One-class SVM[3]也一般被归类为线性模型。
  • 基于相似度衡量的模型 (proximity based models) : 异常点因为和正常点的分布不同, 因此相似度较低, 由此衍生了一系列算法通过相似度来识别异常点
    • 比如最简单的K近邻就可以做异常 检测,一个样本和它第k个近邻的距离就可以被当做是异常值, 显然异常点的k近邻距离更大。
    • 同理, 基于密度分析如LOF [1]、LOCILoOP主要是通过局部的数据密度来检测异常。显然, 异常点所在空间的数据点少, 密度低。
    • 相似的是, Isolation Forest[2]通过划分超平面Q来计算"孤立" 一个样本所需的超平面数量(可以想象成在想吃蛋糕上的樱桃所需的最少刀数)。在密度低的空间里 (异常点所在空间中), 孤例一个样本所需要的划分次数更少。
    • 另一种相似的算法ABOD[7] 是计算每个样本与所有其他样本对所形成的夹角的方差, 异常点因为远离正常点, 因此方差变化 小。换句话说, 大部分异常检测算法都可以被认为是一种估计相似度, 无论是通过密度、距离、 夹角或是划分超平面。通过聚类也可以被理解为一种相似度度量, 比较常见不再赘述。
  • 集成异常检测与模型融合: 在无监督学习时, 提高模型的鲁棒性很重要, 因此集成学习就大有用 武之地。比如上面提到的lsolation Forest, 就是基于构建多棵决策树实现的。最早的集成检测框 架feature bagging[9]与分类问题中的随机森林 (random forest) 很像, 先将训练数据随机划分 (每次选取所有样本的 \(d / 2-d\) 个特征, \(d\) 代表特征数) , 得到多个子训练集, 再在每个训练集上训 练一个独立的模型(默认为LOF)并最终合并所有的模型结果(如通过平均)。值得注意的是, 因为没有标签, 异常检测往往是通过bagging和feature bagging比较多, 而boosting比较少见。 boosting情况下的异常检测, 一般需要生成伪标签Q, 可参靠 \([13,14]\) 。集成异常检测是一个新兴但很有趣的领域, 综述文章可以参考 \([16,17,18]\)
  • 特定领域上的异常检测:比如图像异常检测 [21], 顺序及流数据异常检测(时间序列异常检测) [22], 以及高维空间上的异常检测 [23], 比如前文提到的Isolation Forest就很适合高维数据上的 异常检测。

不难看出, 上文提到的划分标准其实是互相交织的。比如k-近邻可以看做是概率模型非参数化后的 一种变形, 而通过马氏距离计算异常度虽然是线性模型但也对分布有假设(高斯分布)。Isolation Forest虽然是集成学习, 但其实和分析数据的密度有关, 并且适合高维数据上的异常检测。在这种 基础上, 多种算法其实是你中有我, 我中有你, 相似的理念都可以被推广和应用, 比如计算重建误 差不仅可以用PCA, 也可以用神经网络中的auto-encoder。另一种划分异常检测模型的标准可以理 解为局部算法 (local) 和全局算法 (global), 这种划分方法是考虑到异常点的特性。想要了解更多异常检测还是推荐看经典教科书Outlier Analysis [6], 或者综述文章[15]。

虽然一直有新的算法被提出, 但因为需要采用无监督学习, 且我们对数据分布的有限了解, 模型选 择往往还是采用试错法, 因此快速迭代地尝试大量的算法就是一个必经之路。在这个回答下, 我们 会对比多种算法的预测能力、运算开销及模型特点。如无特别说明,本文中的图片、代码均来自于 开源Python异常检测工具库Pyod。文中实验所使用的17个数据集均来自于 (ODDS - Outlier Detection DataSets) 。

参考文献

  • 「异常检测」开源工具库推荐 - 微调的文章 - 知乎 https://zhuanlan.zhihu.com/p/37132428

  • 数据挖掘中常见的「异常检测」算法有哪些? - 微调的回答 - 知乎 https://www.zhihu.com/question/280696035/answer/417091151

  • 不得不推荐这门课:CS259D: Data Mining for Cyber Security 虽然是网络安全方面的应用,但是方法都是通用的,看来也很有启发。https://leotsui.gitbooks.io/cs259d-notes-cn/content/

  • 中科院在读美女博士带你全面了解“异常检测”领域 - 王晋东不在家的文章 - 知乎 https://zhuanlan.zhihu.com/p/260651151

  • Python 时间序列异常检测 ADTK:https://blog.csdn.net/BF02jgtRS00XKtCx/article/details/115343456

  • awesome-TS-anomaly-detection:https://github.com/rob-med/awesome-TS-anomaly-detection

一、HBOS

HBOS全名为:Histogram-based Outlier Score。它是一种单变量方法的组合,不能对特征之间的依赖关系进行建模,但是计算速度较快,对大数据集友好,其基本假设是数据集的每个维度相互独立,然后对每个维度进行区间(bin)划分,区间的密度越高,异常评分越低。理解了这句话,基本就理解了这个算法。

1.1 HBOS算法流程

1、静态宽度直方图

标准的直方图构建方法,在值范围内使用k个等宽箱,样本落入每个箱的频率(相对数量)作为密度(箱子高度)的估计,时间复杂度:O(n)

注意:等宽分箱,每个箱中的数据宽度相同,不是指数据个数相同。例如序列[5,10,11,13,15,35,50,55,72,92,204,215],数据集中最大值是215,最小值是5,分成3个箱,故每个箱的宽度应该为(215-5)/3=70,所以箱的宽度是70,这就要求箱中数据之差不能超过70,并且要把不超过70的数据全放在一起,最后的分箱结果如下:

箱一:5,10,11,13,15,35,50,55,72;箱二:92;箱三:204,215

图片

2、动态宽度直方图

首先对所有值进行排序,然后固定数量的N/k 个连续值装进一个箱里,其 中N是总实例数,k是箱个数,直方图中的箱面积表示实例数,因为箱的宽度是由箱中第一个值和最后一个值决定的,所有箱的面积都一样,因此每一个箱的高度都是可计算的。这意味着跨度大的箱的高度低,即密度小,只有一种情况例外,超过k个数相等,此时允许在同一个箱里超过N/k值,时间复杂度:O(n×log(n))

还是用序列[5,10,11,13,15,35,50,55,72,92,204,215]举例,也是假如分3箱,那么每箱都是4个,宽度为边缘之差,第一个差为15-5=10,第二差为72-35=37,第三个箱宽为215-92=123,为了保持面积相等,所以导致后面的很矮,前面的比较高,如下图所示(非严格按照规则):

图片

3、算法推导过程

对每个维度都计算了一个独立的直方图,其中每个箱子的高度表示密度的估计,然后为了使得最大高度为1(确保了每个特征与异常值得分的权重相等),对直方图进行归一化处理。最后,每一个实例的HBOS值由以下公式计算: \[ H B O S(p)=\sum_{i=0}^d \log \left(\frac{1}{\operatorname{hist}_i(p)}\right) \] 推导过程: 假设样本 \(\mathrm{p}\)\(\mathrm{i}\) 个特征的概率密度为 pi ( \(p\) ) , 则p的概率密度可以计算为, \(d\) 为总的特征的个数: \[ P(p)=P_1(p) P_2(p) \cdots P_d(p) \] 两边取对数: \[ \log (P(p))=\log \left(P_1(p) P_2(p) \cdots P_d(p)\right)=\sum_{i=1}^d \log \left(P_i(p)\right) \] 概率密度越大,异常评分越小,为了方便评分,两边乘以“-1”: \[ -\log (P(p))=-1 \sum_{i=1}^d \log \left(P_t(p)\right)=\sum_{i=1}^d \frac{1}{\log \left(P_i(p)\right)} \] 最后可得: \[ H B O S(p)=-\log (P(p))=\sum_{i=1}^d \frac{1}{\log \left(P_i(p)\right)} \] PyOD是一个可扩展的Python工具包,用于检测多变量数据中的异常值。它可以在一个详细记录API下访问大约 20 个离群值检测算法。

三、 XGBOD

【异常检测】XGBOD:用无监督表示学习改进有监督异常检测

四、COPOD:用「统计」+「机器学习」检测异常

COPOD:用「统计」+「机器学习」检测异常

五、More about Anomaly Detection

那这个异常检测啊,其实也是另外一门学问,那我们课堂上就没有时间讲了,异常检测不是只能用 Aauto-Encoder 这个技术,Aauto-Encoder 这个技术,只是众多可能方法里面的其中一个,我们拿它来当做 Aauto-Encoder 的作业,因为我相信,你未来有很多的机会用得上异常检测这个技术,那实际上有关异常检测更完整的介绍,我们把过去上课的录影放在这边,给大家参考,

Part 1: https://youtu.be/gDp2LXGnVLQ

  • 简介

Part 2: https://youtu.be/cYrNjLxkoXs

  • 信心分数

Part 3: https://youtu.be/ueDlm2FkCnw

  • 异常检测系统的评估?

    • no ACC

    • cost loss设计

    • RUC

Part 4: https://youtu.be/XwkHOUPbc0Q

Part 5: https://youtu.be/Fh1xFBktRLQ

  • 无监督

Part 6: https://youtu.be/LmFWzmn2rFY

Part 7: https://youtu.be/6W8FqUGYyDo

那以上就是有关 Aauto-Encoder 的部分

损失函数

机器学习中的监督学习本质上是给定一系列训练样本 \(\left(x_i, y_i\right)\), 尝试学习 \(x \rightarrow y\) 的映射关系, 使得给定一个 \(x\) , 即便这个 \(x\) 不在训练样本中, 也能够得到尽量接近真实 \(y\) 的输出 \(\hat{y}\) 。而损失函数 (Loss Function) 则是这 个过程中关键的一个组成部分, 用来衡量模型的输出 \(\hat{y}\) 与真实的 \(y\) 之间的差距, 给模型的优化指明方向。

本文将介绍机器学习、深度学习中分类与回归常用的几种损失函数, 包括均方差损失 Mean Squared Loss、平 均绝对误差损失 Mean Absolute Error Loss、Huber Loss、分位数损失 Quantile Loss、交叉樀损失函数 Cross Entropy Loss、Hinge 损失 Hinge Loss。主要介绍各种损失函数的基本形式、原理、特点等方面。

img

前言

在正文开始之前, 先说下关于 Loss Function、Cost Function 和 Objective Function 的区别和联系。在机器学习 的语境下这三个术语经常被交叉使用。

  • 损失函数 Loss Function 通常是针对单个训练样本而言, 给定一个模型输出 \(\hat{y}\) 和一个真实 \(y\), 损失函数输 出一个实值损失 \(L=f\left(y_i, \hat{y_i}\right)\)
  • 代价函数 Cost Function 通常是针对整个训练集(或者在使用 mini-batch gradient descent 时一个 minibatch)的总损失 \(J=\sum_{i=1}^N f\left(y_i, \hat{y}_i\right)\)
  • 目标函数 Objective Function 是一个更通用的术语, 表示任意希望被优化的函数, 用于机器学习领域和非机 器学习领域 (比如运筹优化)

一句话总结三者的关系就是: A loss function is a part of a cost function which is a type of an objective function.

由于损失函数和代价函数只是在针对样本集上有区别,因此在本文中统一使用了损失函数这个术语,但下文的相关公式实际上采用的是代价函数 Cost Function 的形式,请读者自行留意。

结构风险函数

损失函数(loss function)是用来估量模型的预测值f(x)与真实值\(Y\)不一致的程度,它是一个非负实数值函数,通常使用\(L(Y,f(x))\)来表示,损失函数越小,模型的鲁棒性就越好。损失函数是经验风险函数的核心部分,也是结构风险函数的重要组成部分。模型的结构风险函数包括了经验风险项和正则项,通常可以表示成如下的式子:

image-20220821223950768

前面的均值函数表示的是经验风险函数,L代表的是损失函数,后面的Φ是正则化项(regularizer)或者叫惩罚项(penalty term),它可以是L1,也可以是L2等其他的正则函数。整个式子表示的意思是找到使目标函数最小时的θ值。下面列出集中常见的损失函数。

一、 对数损失函数(逻辑回归)MLE 【交叉熵损失函数】

https://zhuanlan.zhihu.com/p/52100927

一文搞懂交叉熵在机器学习中的使用,透彻理解交叉熵背后的直觉

损失函数|交叉熵损失函数

有些人可能觉得逻辑回归的损失函数就是平方损失,其实并不是。平方损失函数可以通过线性回归在假设样本是高斯分布的条件下推导得到,而逻辑回归得到的并不是平方损失。在逻辑回归的推导中,它假设样本服从伯努利分布(0-1分布),然后求得满足该分布的似然函数,接着取对数求极值等等。而逻辑回归并没有求似然函数的极值,而是把极大化当做是一种思想,进而推导出它的经验风险函数为:

最小化负的似然函数(即\(maxF(y,f(x))—>min−F(y,f(x))\))。从损失函数的视角来看,它就成了log损失函数了。

原理解释1:==条件概率下方便计算极大似然估计==

Log损失函数的标准形式:

\(L(Y,P(Y|X))=−logP(Y|X)\)

刚刚说到,取对数是为了方便计算极大似然估计,因为在MLE中,直接求导比较困难,所以通常都是先取对数再求导找极值点。损失函数\(L(Y.P(Y|X))\)表达的是样本在分类\(Y\)的情况下,使概率\(P(Y|X)\)达到最大值(换言之,就是利用已知的样本分布,找到最有可能(即最大概率)导致这种分布的参数值;或者什么样的参数才能使我们观测到目前这组数据的概率最大)。因为log函数是单调递增的,所以\(logP(Y|X)\)也会达到最大值,因此在前面加上负号之后,最大化\(P(Y|X)\)就等价于最小化\(L\)了。

logistic回归\(P(y|x)\)表达式如下(为了将类别标签y统一为1和0,下面将表达式分开表示):

image-20220322202521355

将上面的公式合并在一起,可得到第i个样本正确预测的概率:

image-20220322202548296

上式是对一个样本进行建模的数据表达。对于所有的样本,假设每条样本生成过程独立,在整个样本空间中(N个样本)的概率分布为:

image-20220322202618734

将上式代入到对数损失函数中,得到最终的损失函数为:

image-20220322202653661

原理解释2:相对熵(KL散度)推理

相对熵又称KL散度,如果我们对于同一个随机变量 x 有两个单独的概率分布 P(x) 和 Q(x),我们可以使用 KL 散度(Kullback-Leibler (KL) divergence)来衡量这两个分布的差异.\(DKL\)的值越小,表示q分布和p分布越接近.

相对熵:

image-20220330133351613

相对熵 = 信息熵 + 交叉熵 :

image-20220330134202064

【对数损失函数(Log loss function)】和【交叉熵损失函数(Cross-entroy loss funtion)】在很多文献内是一致的,因为他们的表示式的本质是一样的。

二、 平方损失函数(线性回归,GBDT,最小二乘法,Ordinary Least Squares)MSE

最小二乘法是线性回归的一种,OLS 将问题转化成了一个凸优化问题。在线性回归中,它假设样本和噪声都服从高斯分布(为什么假设成高斯分布呢?其实这里隐藏了一个小知识点,就是中心极限定理,可以参考【central limit theorem】),最后通过极大似然估计(MLE)可以推导出最小二乘式子。最小二乘的基本原则是:最优拟合直线应该是使各点到回归直线的距离和最小的直线,即平方和最小。换言之,OLS是基于距离的,而这个距离就是我们用的最多的欧几里得距离。为什么它会选择使用欧式距离作为误差度量呢(即Mean squared error, MSE),主要有以下几个原因:

  • 简单,计算方便;
  • 欧氏距离是一种很好的相似性度量标准;
  • 在不同的表示域变换后特征性质不变。

平方损失(Square loss)的标准形式如下:\(L(Y,f(X))=(Y−f(x))^2\)当样本个数为n时,此时的损失函数变为:

image-20220322202912962

\(Y−f(X)\) 表示的是残差,整个式子表示的是残差的平方和,而我们的目的就是最小化这个目标函数值(注:该式子未加入正则项),也就是最小化残差的平方和(residual sum of squares,RSS)。

而在实际应用中,通常会使用均方差(MSE)作为一项衡量指标,公式如下:

image-20220322202957484

三、 指数损失函数(Adaboost)

Adaboost训练误差以指数下降。所以说,指数损失本身并没有带来优化上的特殊,优点在于计算和表达简单。

学过Adaboost算法的人都知道,它是前向分步加法算法的特例,是一个加和模型,损失函数就是指数函数。在Adaboost中,经过m此迭代之后,可以得到\(fm(x)\):

image-20220322203050695

Adaboost每次迭代时的目的是为了找到最小化下列式子时的参数 \(a\)\(G\)

image-20220322203141435

而指数损失函数(exp-loss)的标准形式如下:

image-20220322203221432

可以看出,Adaboost的目标式子就是指数损失,在给定N个样本的情况下,Adaboost的损失函数为:

image-20220322203238853

四、 ==Hinge 合页损失函数==(SVM,advGAN)

image-20220401165315551

线性支持向量机学习除了原始最优化问题,还有另外一种解释,就是最优化以下目标函数:

image-20220322205741804

目标函数的第一项是经验损失或经验风险函数:

image-20220322205801232

称为合页损失函数(hinge loss function)。下标”+”表示以下取正值的函数:

image-20220322205844003

这就是说,当样本点\((xi,yi)\)被正确分类且函数间隔(确信度)\(yi(w·xi+b)\)大于1时,损失是0,否则损失是\(1−yi(w·xi+b)\)。目标函数的第二项是系数为 \(λ\)\(w\)\(L2\) 范数,是正则化项。

接下来证明线性支持向量机原始最优化问题:

image-20220322210000477

image-20220322210121285

先令\([1−yi(w·xi+b)]+=ξi\),则\(ξi≥0\),第二个约束条件成立;由\([1−yi(w·xi+b)]+=ξi\),当\(1−yi(w·xi+b)>0\)时,有\(yi(w·xi+b)=1−ξi\);当\(1−yi(w·xi+b)≤0\)时,\(ξi=0\),有\(yi(w·xi+b)≥1−ξi\),所以第一个约束条件成立。所以两个约束条件都满足,最优化问题可以写作

image-20220322210943775

若取 \(λ=1/2C\) 则:

image-20220322211012150

五、Softmax函数和Sigmoid函数的区别与联系

https://zhuanlan.zhihu.com/p/356976844

5.1 分类任务

sigmoid

Sigmoid ===多标签分类问题===多个正确答案=非独占输出(例如胸部X光检查、住院)。构建分类器,解决有多个正确答案的问题时,用Sigmoid函数分别处理各个原始输出值。

Softmax =多类别分类问题=只有一个正确答案=互斥输出(例如手写数字,鸢尾花)。构建分类器,解决只有唯一正确答案的问题时,用Softmax函数处理各个原始输出值。Softmax函数的分母综合了原始输出值的所有因素,这意味着,Softmax函数得到的不同概率之间相互关联。

Sigmoid函数是一种logistic函数,它将任意的值转换到 [公式] 之间,如图1所示,函数表达式为: [公式]

它的导函数为: [公式]

img

优点

  1. Sigmoid函数的输出在(0,1)之间,输出范围有限,优化稳定,可以用作输出层
  2. 连续函数,便于求导

缺点

  1. 最明显的就是饱和性,从上图也不难看出其两侧导数逐渐趋近于0,容易造成梯度消失

2.激活函数的偏移现象。Sigmoid函数的输出值均大于0,使得输出不是0的均值,这会导致后一层的神经元将得到上一层非0均值的信号作为输入,这会对梯度产生影响。

  1. 计算复杂度高,因为Sigmoid函数是指数形式。

Softmax

Softmax函数,又称归一化指数函数,函数表达式为: [公式]

img

Softmax函数是二分类函数Sigmoid在多分类上的推广,目的是将多分类的结果以概率的形式展现出来。如图2所示,Softmax直白来说就是将原来输出是3,1,-3通过Softmax函数一作用,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们就可以将它理解成概率,在最后选取输出结点的时候,我们就可以选取概率最大(也就是值对应最大的)结点,作为我们的预测目标。

由于Softmax函数先拉大了输入向量元素之间的差异(通过指数函数),然后才归一化为一个概率分布,在应用到分类问题时,它使得各个类别的概率差异比较显著,最大值产生的概率更接近1,这样输出分布的形式更接近真实分布。

Softmax可以由三个不同的角度来解释。从不同角度来看softmax函数,可以对其应用场景有更深刻的理解:

  1. softmax可以当作argmax的一种平滑近似,与arg max操作中暴力地选出一个最大值(产生一个one-hot向量)不同,softmax将这种输出作了一定的平滑,即将one-hot输出中最大值对应的1按输入元素值的大小分配给其他位置。
  2. softmax将输入向量归一化映射到一个类别概率分布,即 [公式] 个类别上的概率分布(前文也有提到)。这也是为什么在深度学习中常常将softmax作为MLP的最后一层,并配合以交叉熵损失函数(对分布间差异的一种度量)。
  3. 概率图模型的角度来看,softmax的这种形式可以理解为一个概率无向图上的联合概率。因此你会发现,条件最大熵模型与softmax回归模型实际上是一致的,诸如这样的例子还有很多。由于概率图模型很大程度上借用了一些热力学系统的理论,因此也可以从物理系统的角度赋予softmax一定的内涵。

5.2 总结

  1. 如果模型输出为非互斥类别,且可以同时选择多个类别,则采用Sigmoid函数计算该网络的原始输出值。
  2. 如果模型输出为互斥类别,且只能选择一个类别,则采用Softmax函数计算该网络的原始输出值。
  3. Sigmoid函数可以用来解决多标签问题Softmax函数用来解决单标签问题
  4. 对于某个分类场景,当Softmax函数能用时,Sigmoid函数一定可以用。

6 损失函数Q&A

==平方误差损失函数和交叉熵损失函数分别适合什么场景?==

一般还说,平方损失函数更适合输出为连续,并且最后一层不含sigmod或softmax激活函数的神经网络;交叉熵损失函数更适合二分类或多分类的场景。

一、相似性度量指标

机器学习中的相似性度量方法 - 天下客的文章 - 知乎 https://zhuanlan.zhihu.com/p/411876558

描述样本之间相似度的方法有很多种,一般来说常用的有相关系数和欧式距离。本文对机器学习中常用的相似性度量方法进行了总结。在做分类时,常常需要估算不同样本之间的相似性度量(Similarity Measurement),这时通常采用的方法就是计算样本间的“距离”(distance)。采用什么样的方法计算距离是很讲究的,甚至关系到分类的正确与否。

  • 欧式距离:k-means
  • 曼哈顿距离
  • 切比雪夫距离
  • 闵可夫斯基距离
  • 标准化欧氏距离
  • 马氏距离
  • 夹角余弦
  • 汉明距离:simhash
  • 杰卡德距离&杰卡德相似系数: 杰卡德相似系数是衡量两个集合的相似度一种指标。
  • 相关系数&相关距离
  • 信息熵

二、推荐算法评价指标

  • 推荐算法评价指标 - 一干正事就犯困的文章 - 知乎 https://zhuanlan.zhihu.com/p/359528909

2.1 AP

AP 衡量的是训练好的模型在每个类别上的好坏;

img

AP总结了一个精确召回曲线,作为在每个阈值处获得的精度的加权平均值, 并且与以前的阈值相比, 召回率的增 加用作权重: \[ A P=\sum_n\left(R_n-R_{(n-1))} P_n)\right. \] 其中和分别是第 \(\mathrm{n}\) 个阈值 1 时的精度和召回率。此实现末进行揷值, 并且与使用梯形规则计算精确调用曲线下的面 积有所不同,后者使用线性揷值并且可能过于乐观。

2.2 MAP

MAP (Mean Average Precision) 常用于排序任务,MAP的计算涉及另外两个指标:Precision和Recall

  • Precision和Precision@k, 推荐算法中的精度precision计算如下:

\[ \text { precision }=\frac{\text { 算法结果中相关的item数量 }}{\text { 推荐的item总数量 }} \]

可以看出Precision的计算没有考虑结果列表中item的顺序,Precision@k则通过切片的方式将顺序隐含在结果 中。Precision@k表示列表前k项的Precision, 随着k的变化, 可以得到一系列precision值, 用 \(P(k)\) 表示。

  • Recall和Recall@k, 推荐算法中的召回率recall计算如下:

\[ \text { recall }=\frac{\text { 算法结果中相关的 } i t e m \text { 数量 }}{\text { 所有相关的item数量 }} \]

与Precision@kk相似, recall@k表示结果列表前k项的recall, 随着k的变化, 可以得到一系列的recall值, 用 \(r(k)\) 表示。

  • AP@N, AP (Average Precision) 平均精度的计算以Precision@k为基础, 可以体现出结果列表中item顺序的重要性, 其 计算过程如下:

\[ A P @ N=\frac{1}{m} \sum_{k=1}^N(P(k) \quad \text { if kth item is relevant })=\frac{1}{m} \sum_{k=1}^N P(k) \cdot r e l(k) \]

其中, \(\mathrm{N}\) 表示要求推荐的 \(\mathrm{N}\) 个item, \(\mathrm{m}\) 表示所有相关的item总数, \(r e l(k)\) 表示第 \(k\) 个item是否相关, 相关为 1 , 反 之为 0

AP@N的值越大,表示推荐列表中相关的item数量越多以及相关item的排名越靠前

  • MAP@N

AP@N评价了算法对单个用户的性能,MAP@N则是算法对多个用户的平均值,是平均数的平均,其计算过程如下\[ M A P @ N=\frac{1}{|U|} \sum_{u=1}^{|U|}(A P @ N) u=\frac{1}{|U|} \sum u=1^{|U|}\left(\frac{1}{m} \sum_{k=1}^N P_u(k) \cdot r e l_u(k)\right) \]

一、二分类问题

阈值调节问题?

image-20220421165422230

  • 准确率 (Accuracy)预测正确的概率(TP+TN)/(TP+TN+FP+FN)
  • 精确率(查准率 Precision ):预测为正的样本中实际为正的样本的概率TP/(TP+FP)
  • 错误发现率(FDR)= 1 - 精确率 = 预测为正的样本中实际为负的样本的概率 【FP/(TP+FP)
  • 召回率(查全率)- Recall实际为正的样本中被预测为正样本的概率TP/(TP+FN)
  • 真正率(TPR) = 灵敏度(召回率) = TP/(TP+FN)
  • 假正率(FPR) = 1- 特异度 = FP/(FP+TN)
  • F1=是准确率和召回率的调和平均值 (2×Precision×Recall)/(Precision+Recall)
  • G-mean \((\mathrm{GM})=\) 是准确率和召回率的几何平均值 \(G-\) mean \(=\sqrt{\text { Recall } \cdot \text { Precision }}\)

image-20220421165436795

1.1 F1

精确率(Precision)和召回率(Recall)之间的关系用图来表达,就是下面的PR曲线。可以发现他们俩的关系是「两难全」的关系。为了综合两者的表现,在两者之间找一个平衡点,就出现了一个 F1分数。

F1=(2×Precision×Recall) /(Precision+Recall)

P意义类似于每通过准确预测得到TP个正例需要TP+FP个预测类别为正例的样本。

R意义类似于每通过成功召回得到TP个正例需要TP+FN个真实类别为正例的样本。

F1度量了给定一批样本,对这一批样本进行预测与召回,最终得到的正例的多少。其中一半的正例是通过预测得到的,一半的正例是通过召回得到的。

有一种把预测所需的预测类别为正例的样本和召回所需的真实类别为正例的样本看作原料,而我们的目标正例样本看作产品的感觉。所以也能解释为什么P跟R其中一者比较低的时候,F1会偏低。因为跟算术平均数不一样,两者不能互相替代,两部分各负责一半。那么加权调和平均Fbeta也可以很好的理解了。

\[ \frac{1}{F_\beta}=\frac{1}{1+\beta^2} \cdot\left(\frac{1}{P}+\frac{\beta^2}{R}\right) \]

各自负责的比例不一样了。因此beta越大,Fbeta越着重考虑召回能力。

1.2 ROC/AUC的概念

(1)灵敏度,特异度,真正率,假正率

在正式介绍ROC/AUC之前,我们还要再介绍两个指标,这两个指标的选择也正是ROC和AUC可以无视样本不平衡的原因。 这两个指标分别是:灵敏度和(1-特异度),也叫做真正率(TPR)和假正率(FPR)。其实我们可以发现灵敏度和召回率是一模一样的,只是名字换了而已。由于我们比较关心正样本,所以需要查看有多少负样本被错误地预测为正样本,所以使用(1-特异度),而不是特异度。

真正率(TPR) = 灵敏度(召回率) = TP/(TP+FN)

假正率(FPR) = 1- 特异度 = FP/(FP+TN)

下面是真正率和假正率的示意,我们发现TPR和FPR分别是基于实际表现1和0出发的,也就是说它们分别在实际的正样本和负样本中来观察相关概率问题。

正因为如此,所以无论样本是否平衡,都不会被影响。还是拿之前的例子,总样本中,90%是正样本,10%是负样本。我们知道用准确率是有水分的,但是用TPR和FPR不一样。这里,TPR只关注90%正样本中有多少是被真正覆盖的,而与那10%毫无关系,同理,FPR只关注10%负样本中有多少是被错误覆盖的,也与那90%毫无关系,

如果我们从实际表现的各个结果角度出发,就可以避免样本不平衡的问题了,这也是为什么选用TPR和FPR作为ROC/AUC的指标的原因。

(2)ROC(接受者操作特征曲线)

ROC(Receiver Operating Characteristic)曲线,又称接受者操作特征曲线。该曲线最早应用于雷达信号检测领域,用于区分信号与噪声。后来人们将其用于评价模型的预测能力,ROC曲线是基于混淆矩阵得出的。

ROC曲线中的主要两个指标就是真正率假正率, 上面也解释了这么选择的好处所在。其中横坐标为假正率(FPR),纵坐标为真正率(TPR),下面就是一个标准的ROC曲线图。

(3)AUC的缺陷?

优点:目前普遍认为接收器工作特性曲线(ROC)曲线下的面积—AUC是评估分类模型准确性的标准方法。它避免了在阈值选择过程中假定的主观性,当连续的概率得到的分数被转换为二分类标签时,通过总结整体模型表现,其衡量模型区分正负样本的性能优于通过阈值来判断的其他方法(比如准确率、召回率等)。

  • 忽略了预测的概率值和模型的拟合优度
  • AUC反应了太过笼统的信息。无法反应召回率、精确率等在实际业务中经常关心的指标
  • 对FPR和TPR两种错误的代价同等看待
  • 它没有给出模型误差的空间分布信息
  • 最重要的一点,AUC的misleading的问题

auc仅反应模型的排序能力,无法反应模型的拟合优度;auc很多时候无法直接反应细粒度的和业务目标更相关的metric信息,例如 top k的准确率,召回率等等(例如同auc的模型在不同的区间的预测能力是存在差别的);

1.3、K-S曲线

K-S曲线, 又称作洛伦兹曲线。实际上, K-S曲线的数据来源以及本质和ROC曲线是一致的, 只是ROC曲线是把真 正率 \((T P R)\) 和假正率 \((F P R)\) 当作横纵轴, 而K-S曲线是把真正率 \((T P R)\) 和假正率 \((F P R\) ) 都当作是 纵轴,横轴则由选定的阈值来充当。从 K-S 曲线就能衍生出 \(K S\) 值, \(K S=\max (T P R-F P R)\) ,即是两条曲线 之间的最大间隔距离。

K-S曲线的画法:

  1. 排序: 对于二元分类器来说, 模型训练完成之后每个样本都会得到一个类概率值, 把样本按这个类概率值从大 到小进行排序;
  2. 找阈值: 取排序后前 \(10 \% \times k(k=1,2,3, \ldots, 9)\) 处的值(概率值)作为阈值, 分别计算出不同的 \(T P R\)\(F P R\) 值, 以 \(10 \% \times k(k=1,2,3, \ldots, 9)\) 为横坐标, 分别以 \(T P R\)\(F P R\) 值为纵坐标, 就可以画出两个曲 线,这就是K-S曲线,类似于下图。
  3. KS值:从 K-S 曲线就能衍生出 \(K S\) 值, \(K S=\max (T P R-F P R)\) ,即是两条曲线之间的最大间隔距离。KS值越大 表示模型 的区分能力越强。

img

1.4 Lift曲线

Lift曲线它衡量的是,与不利用模型相比,模型的预测能力“变好”了多少,lift(提升指数)越大,模型的运行效果越好。实质上它强调的是投入与产出比

tip:理解Lift可以先看一下Quora上的一篇文章:What's Lift curve?

Lift计算公式:先介绍几个相关的指标,以免混淆:

  • 准确率 (accuracy, ACC) :

\[ A C C=\frac{T P+T N}{F P+F N+T P+T N} \]

  • 正确率(Precision, PRE), 查准率:

\[ P R E=\frac{T P}{T P+F P} \]

  • 真阳性率(True Positive Rate, TPR),灵敏度(Sensitivity),召回率(Recall):

\[ T P R=\frac{T P}{T P+F N} \]

  • 假阳性率(False Positice Rate, FPR), 误诊率( = 1 - 特异度):

\[ F P R=\frac{F P}{F P+T N} \]

Lift计算公式: \[ L i f t=\frac{\frac{T P}{T P+F P}}{\frac{T P+F N}{T P+F P+T N+F N}}=\frac{P R E}{\text { 正例占比 }} \] 根据以上公式可知, Lift指标可以这样理解: 在不使用模型的情况下, 我们用先验概率估计正例的比例, 即上式子分母部分, 以此作为正例的命中率; 利用模型后, 我们不需要从整个样本中来挑选正例, 只需要从我们预测为正例 的那个样本的子集 \(T P+F P\) 中挑选正例, 这时正例的命中率为 \(P R E\), 后者除以前者即可得提升值Lift 。

Lift曲线:

为了作出LIft曲线,首先引入 depth 的概念: \[ \operatorname{depth}=\frac{T P+F P}{T P+F P+T N+F N} \] 从公式可以看出, depth 代表的是预测为正例的样本占整个样本的比例。

当阈值为 0 时, 所有的样本都被预测为正例, 因此 \(\operatorname{depth}=1\), 于是 \(L i f t=1\), 模型末起提升作用。随着阈值逐 渐增大, 被预测为正例的样本数逐渐减少, depth 减小, 而较少的预测正例样本中的真实正例比例逐渐增大。当阈 值增大至1时, 没有样本被预测为正例, 此时 depth \(=0\), 而 Lift \(=0\) 。由此可见, Lift 与 depth 存在相反方 向变化的关系。在此基础上作出 Lift 图:

img

一般要求, 在尽量大的 depth 下得到尽量大的 Lift, 所以 Lift 曲线的右半部分应该尽量陡峭。

1.5 P-R曲线

  • 精确率(查准率)- Precision :预测为正的样本中实际为正的样本的概率TP/(TP+FP)

  • 召回率(查全率)- Recall实际为正的样本中被预测为正样本的概率TP/(TP+FN)

P-R曲线刻画查准率查全率(召回率)之间的关系,查准率指的是在所有预测为正例的数据中,真正例所占的比例,查全率是指预测为真正例的数据占所有正例数据的比例。查准率和查全率是一对矛盾的度量,一般来说,查准率高时,查全率往往偏低,查全率高时,查准率往往偏低。

在很多情况下,我们可以根据学习器的预测结果对样例进行排序,排在前面的是学习器认为最可能是正例的样本,排在后面的是学习器认为最不可能是正例的样本,按此顺序逐个把样本作为正例进行预测,则每次可计算当前的查全率和查准率,以查准率为y轴,以查全率为x轴,可以画出下面的P-R曲线。

img

如果一个学习器的P-R曲线被另一个学习器的P-R曲线完全包住,则可断言后者的性能优于前者,当然我们可以根据曲线下方的面积大小来进行比较,但更常用的是平衡点或者是F1值。

  • 平衡点(BEP)是查准率=查全率时的取值,如果这个值较大,则说明学习器的性能较好。F1值越大,我们可以认为该学习器的性能较好。
  • F1度量BEP过于简单,这个平衡点是建立在”查准率=查全率“的前提下,无法满足实际不同场景的应用。

我们先来引入加权调和平均: \(F_\beta\) : \[ \frac{1}{F_\beta}=\frac{1}{1+\beta^2}\left(\frac{1}{P}+\frac{\beta^2}{R}\right) \text { 公式 }(1) \] 加权调和平均与算术平均 \(\frac{P+R}{2}\) 和几何平均 \(\sqrt{P+R}\) 相比, 调和平均更重视较小值(这可以从倒数上看出 来)。当 \(\beta=1\), 即F1是基于查准率和查全率的调和平均定义的, \(\mathrm{F} 1\) 的公式如下: \[ \frac{1}{F_1}=\frac{1}{2}\left(\frac{1}{P}+\frac{1}{R}\right) \] 我们把公式求倒数,即可得: \[ F 1=\frac{2 * P * R}{P+R} \] 在一些应用中, 对查准率和查全率的重视程度不同。例如在商品推荐中, 为了尽可能少打扰用户, 更希望推荐的内 容确实是用户感兴趣的, 此时查准率更重要; 而在罪犯信息检索或者病人检查系统中, 更希望尽可能少的漏判, 此 时查全率更重要。F1度量的一般形式是 \(F_\beta\) ,能让我们自定义对查准率/查全率的不同偏好: \[ F_\beta=\frac{\left(1+\beta^2\right) * P * R}{\left(\beta^2 * P\right)+R} \] 其中, \(\beta>0\) 度量了查全率对查准率的相对重要性 (不明白的同学可以回看公式1), \(\beta=1\) 时退化为标准F1, \(\beta>1\) 时查全率有更大影响; \(\beta<1\) 时, 查准率有更大影响。

1.6 对数损失(Log Loss)

AUC ROC考虑用于确定模型性能的预测概率然而, AUC ROC存在问题, 它只考虑概率的顺序, 因此没有考虑模型预测更可能为正样本的更高概率的能力(即考虑了大小, 但没有考虑更高精度)。在这种情况下, 我们可以使用对数损失, 即每个实例的正例预测概率的对数的负平均值。

对数损失 (Logistic Loss, logloss) 是对预测概率的似然估计,其标准形式为: \[ \log \operatorname{loss}=\log P(Y \mid X) \] 对数损失最小化本质是上利用样本中的已知分布, 求解拟合这种分布的最佳模型参数, 使这种分布出现概率最大。

对数损失对应的二分类的计算公式为: \[ \log \operatorname{loss}=-\frac{1}{N} \sum_{i=1}^N\left(y_i \log \hat{y}_i+\left(1-y_i\right) \log \left(1-\hat{y}_i\right)\right), \quad y \in[0,1] \] 其中 \(\mathrm{N}\) 为样本数, \(\hat{y}_i\) 为预测为 1 的概率。对数损失在多分类问题中也可以使用,其计算公式为: \[ \log \operatorname{loss}=-\frac{1}{N} \frac{1}{C} \sum_{i=1}^N \sum_{j=1}^C\left(y_{i j} \log \hat{y_{i j}}\right), \quad y \in[0,1] \] 其中, \(\mathrm{N}\) 为样本数, C为类别数, logloss衡量的是预测概率分布和真实概率分布的差异性, 取值越小越好。

1.7 多分类

很多时候我们有多个二分类混洧矩阵,例如进行多次训练测试,每次得到一个混淆矩阵;或是在多个数据集上进 行训练测试,希望估计算法的全局性能; 或者是执行分类任务, 每两两类别的组合都对应一个混淆矩阵; 总之是在 \(\mathrm{n}\) 个分类混淆矩阵上综合考察查准率和查全率

  • 宏观: 在各个混淆军阵上分别计算出查准率和查全率, 记为 \((P 1, R 1),(P 2, R 2), \ldots(\mathrm{Pn}, \mathrm{Rn})\), 在计算平均值, 这样 就得到“宏观查准率"(macro-P), “宏观查全率”(macro-R)、“宏观F1"(macro-F1):

\[ \begin{gathered} \text { macro }-P=\frac{1}{n} \sum_{i=1}^n P_i \\ \text { macro }-R=\frac{1}{n} \sum_{i=1}^n R_i \\ \text { macro }-F 1=\frac{2 * \text { macro }-P * \text { macro }-R}{\text { macro }-P+\text { macro }-R} \end{gathered} \]

  • 微观:将个混淆矩阵对应的元素进行平均, 得到TP、FP、TN、FN的平均值, 分别记为 \(\overline{T P} 、 \overline{F P} 、 \overline{F N}\)\(\overline{T N}\), 再基于这些平均值计算出“微观查准率"(micro-P), “微观查全率”(micro-R)、“微观F1"(micro-F1):

\[ \begin{gathered} \text { micro }-P=\frac{\overline{T P}}{\overline{T P}+\overline{F P}} \\ \text { micro }-R=\frac{\overline{T P}}{\overline{T P}+\overline{F N}} \\ \text { micro }-F 1=\frac{2 * \text { micro }-P * \text { micro }-R}{\text { micro }-P+\text { micro }-R} \end{gathered} \]

二、评分总结(sklearn)

sklearn.metrics - 回归/分类模型的评估方法:https://zhuanlan.zhihu.com/p/408078074

2.1 分类模型

accuracy_score

分类准确率分数是指所有分类正确的百分比。分类准确率这一衡量分类器的标准比较容易理解,但是它不能告诉你响应值的潜在分布,并且它也不能告诉你分类器犯错的类型。所以在使用的时候,一般需要搭配matplotlib等数据可视化工具来观察预测的分类情况,与实际的结果做更加直观的比较。

1
2
3
4
5
6
7
8
import numpy as np  
from sklearn.metrics import accuracy_score
y_pred = [0, 2, 1, 3]
y_true = [0, 1, 2, 3]
accuracy_score(y_true, y_pred) # 默认normalization = True
>>> 0.5
accuracy_score(y_true, y_pred, normalize=False)
>>> 2
recall_score

召回率 =提取出的正确信息条数 /样本中的信息条数。通俗地说,就是所有准确的条目有多少被检索出来了。

1
2
recall_score(y_true, y_pred, labels=None, pos_label=1,average='binary', sample_weight=None)
参数average : string, [None, ‘micro’, ‘macro’(default), ‘samples’, ‘weighted’]

将一个二分类matrics拓展到多分类或多标签问题时,我们可以将数据看成多个二分类问题的集合,每个类都是一个二分类。接着,我们可以通过跨多个分类计算每个二分类metrics得分的均值,这在一些情况下很有用。你可以使用average参数来指定。

  • macro:计算二分类metrics的均值,为每个类给出相同权重的分值。
  • weighted:对于不均衡数量的类来说,计算二分类metrics的平均,通过在每个类的score上进行加权实现。
  • micro:给出了每个样本类以及它对整个metrics的贡献的pair(sample-weight),而非对整个类的metrics求和,它会每个类的metrics上的权重及因子进行求和,来计算整个份额。
  • samples:应用在multilabel问题上。它不会计算每个类,相反,它会在评估数据中,通过计算真实类和预测类的差异的metrics,来求平均(sample_weight-weighted)
  • average:average=None将返回一个数组,它包含了每个类的得分.
roc_curve

ROC曲线指受试者工作特征曲线/接收器操作特性(receiver operating characteristic,ROC)曲线,是反映灵敏性和特效性连续变量的综合指标,是用构图法揭示敏感性和特异性的相互关系,它通过将连续变量设定出多个不同的临界值,从而计算出一系列敏感性和特异性。ROC曲线是根据一系列不同的二分类方式(分界值或决定阈),以真正例率(也就是灵敏度)(True Positive Rate,TPR)为纵坐标,假正例率(1-特效性)(False Positive Rate,FPR)为横坐标绘制的曲线。

通过ROC我们可以观察到模型正确识别的正例的比例与模型错误地把负例数据识别成正例的比例之间的权衡。TPR的增加以FPR的增加为代价。ROC曲线下的面积是模型准确率的度量,AUC(Area under roc curve)。

TPR = TP /(TP + FN) (正样本预测数 / 正样本实际数

FPR = FP /(FP + TN) (负样本预测数 /负样本实际数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import numpy as np  
from sklearn import metrics
y = np.array([1, 1, 2, 2])
scores = np.array([0.1, 0.4, 0.35, 0.8])
fpr, tpr, thresholds = metrics.roc_curve(y, scores, pos_label=2)
fpr
>>> array([0. , 0.5, 0.5, 1. ])
tpr
>>> array([0.5, 0.5, 1. , 1. ])
thresholds
>>> array([0.8 , 0.4 , 0.35, 0.1 ])

# check auc score
from sklearn.metrics import auc
metrics.auc(fpr, tpr)
>>> 0.75

# 也可以直接根据预测值+真实值来计算出auc值,略过roc的计算过程
‘’‘
sklearn.metrics.roc_auc_score(y_true, y_score, average='macro', sample_weight=None)
average : string, [None, ‘micro’, ‘macro’(default), ‘samples’, ‘weighted’]
’‘’
# 真实值(必须是二值)、预测值(可以是0/1,也可以是proba值)
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 0, 1, 1])
y_scores = np.array([0.1, 0.4, 0.35, 0.8])
roc_auc_score(y_true, y_scores)
>>> 0.75
confusion metric

混淆矩阵(confusion matrix),又称为可能性表格或是错误矩阵。它是一种特定的矩阵用来呈现算法性能的可视化效果。其每一列代表预测值,每一行代表的是实际的类别。

1
confusion_matric(y_true, y_pred, labels=None, pos_label=1, average='binary', sample_weight=None)
precision_score
1
precision_score(y_true, y_pred, labels=None, pos_label=1, average='binary')
preview

三、评价指标Q&A

精度指标存在的问题
  • 有倾向性的问题。比如,判断空中的飞行物是导弹还是其他飞行物,很显然为了减少损失,我们更倾向于相信是导弹而采用相应的防护措施。此时判断为导弹实际上是其他飞行物与判断为其他飞行物实际上是导弹这两种情况的重要性是不一样的;
  • 样本类别数量严重不均衡的情况。比如银行客户样本中好客户990个,坏客户10个。如果一个模型直接把所有客户都判断为好客户,得到精度为99%,但这显然是没有意义的。
为什么 ROC 和 AUC 都能应用于非均衡的分类问题?

ROC曲线只与横坐标 (FPR) 和 纵坐标 (TPR) 有关系 。我们可以发现TPR只是正样本中预测正确的概率,而FPR只是负样本中预测错误的概率,和正负样本的比例没有关系。因此 ROC 的值与实际的正负样本比例无关,因此既可以用于均衡问题,也可以用于非均衡问题。而 AUC 的几何意义为ROC曲线下的面积,因此也和实际的正负样本比例无关。

参考文献

一、回归问题评价指标

均方差损失 Mean Squared Loss、平均绝对误差损失 Mean Absolute Error Loss、Huber Loss、分位数损失 Quantile Loss

机器学习中的监督学习本质上是给定一系列训练样本 \(\left(x_i, y_i\right)\), 尝试学习 \(x \rightarrow y\) 的映射关系, 使得给定一个 \(x\), 即便这个 \(x\) 不在训练样本中, 也能够得到尽量接近真实 \(y\) 的输出 \(\hat{y}\) 。而损失函数 (Loss Function) 则是这个过 程中关键的一个组成部分, 用来衡量模型的输出 \(\hat{y}\) 与真实的 \(y\) 之间的差距, 给模型的优化指明方向。

1.1 均方差损失 MSE、L2 loss

1.1.1 基本形式与原理

均方差Mean Squared Error (MSE)损失是机器学习、深度学习回归任务中最常用的一种损失函数, 也称为 L2 Loss。其基本形式如下: \[ J_{M S E}=\frac{1}{N} \sum_{i=1}^N\left(y_i-\hat{y_i}\right)^2 \] 从直觉上理解均方差损失,这个损失函数的最小值为 0 (当预测等于真实值时),最大值为无穷大。下图是对于真 实值 \(y=0\), 不同的预测值 \([-1.5,1.5]\) 的均方差损失的变化图。横轴是不同的预测值, 纵轴是均方差损失, 可以 看到随着预测与真实值绝对误差 \(|y-\hat{y}|\) 的增加, 均方差损失呈二次方地增加。

img
1.1.2 背后的假设

【独立同分布-中心极限定理】: 如果 \(\left\{X_n\right\}\) 独立同分布, 且 \(\mathbb{E} X=\mu, \mathbb{D} X=\sigma^2>0\) ,则 \(\mathrm{n}\) 足够大时 \(\bar{X}_n\) 近似服从正态分布 \(N\left(\mu, \frac{\sigma^2}{n}\right)\)\[ \lim _{n \rightarrow \infty} P\left(\frac{\bar{X}_n-\mu}{\sigma / \sqrt{n}}<a\right)=\Phi(a)=\int_{-\infty}^a \frac{1}{\sqrt{2 \pi}} e^{-t^2 / 2} d t \] 实际上在一定的假设下, 我们可以使用最大化似然得到均方差损失的形式。假设模型预测与真实值之间的误差服从标准高斯分布 \((\mu=0, \sigma=1)\) ,则给定一个 \(x_i\) 模型输出真实值 \(y_i\) 的概率为 \[ p\left(y_i \mid x_i\right)=\frac{1}{\sqrt{2 \pi}} \exp \left(-\frac{\left(y_i-\hat{y}_i\right)^2}{2}\right) \] 进一步我们假设数据集中 \(\mathrm{N}\) 个样本点之间相互独立, 则给定所有 \(x\) 输出所有真实值 \(y\) 的概率, 即似然 Likelihood, 为所有 \(p\left(y_i \mid x_i\right)\) 的累乘 \[ L(x, y)=\prod_{i=1}^N \frac{1}{\sqrt{2 \pi}} \exp \left(-\frac{\left(y_i-\hat{y}_i\right)^2}{2}\right) \] 通常为了计算方便,我们通常最大化对数似然Log-Likelihood \[ L L(x, y)=\log (L(x, y))=-\frac{N}{2} \log 2 \pi-\frac{1}{2} \sum_{i=1}^N\left(y_i-\hat{y_i}\right)^2 \] 去掉与 \(\hat{y_i}\) 无关的第一项, 然后转化为最小化负对数似然 Negative Log-Likelihood \[ N L L(x, y)=\frac{1}{2} \sum_{i=1}^N\left(y_i-\hat{y}_i\right)^2 \] 可以看到这个实际上就是均方差损失的形式。也就是说在模型输出与真实值的误差服从高斯分布的假设下, 最小化均方差损失函数与极大似然估计本质上是一致的, 因此在这个假设能被满足的场景中(比如回归), 均方差损失是一个很好的损失函数选择;当这个假设没能被满足的场景中(比如分类),均方差损失不是一 个好的选择。

hulu 百面机器学习 —— 平方根误差的”意外“

95%的时间区间效果很好,RMSE指标居高不下的原因? \[ J_{M S E}=\frac{1}{N} \sum_{i=1}^N\left(y_i-\hat{y_i}\right)^2 \] 一般情况下RSME能反应预测值与真实值的偏离程度,但是易受离群点的影响;

解决方案

  • 数据预处理将噪音去掉
  • 将离群点的产生机制建模进去
  • 更鲁棒的模型评估指标:平均绝对百分比误差(MAPE),分位数损失

1.2 平均绝对误差 MAE

1.2.1 基本形式与原理

平均绝对误差 Mean Absolute Error (MAE) 是另一类常用的损失函数, 也称为 L1 Loss。其基本形式如下 \[ J_{M A E}=\frac{1}{N} \sum_{i=1}^N\left|y_i-\hat{y_i}\right| \] 同样的我们可以对这个损失函数进行可视化如下图, MAE 损失的最小值为 0 (当预测等于真实值时),最大值为 无穷大。可以看到随着预测与真实值绝对误差 \(|y-\hat{y}|\) 的增加, MAE 损失呈线性增长。

img
1.2.2 背后的假设

同样的我们可以在一定的假设下通过最大化似然得到 MAE 损失的形式, 假设模型预测与真实值之间的误差服 从拉普拉斯分布 Laplace distribution \((\mu=0, b=1)\), 则给定一个 \(x_i\) 模型输出真实值 \(y_i\) 的概率为 \[ p\left(y_i \mid x_i\right)=\frac{1}{2} \exp \left(-\left|y_i-\hat{y_i}\right|\right) \] 与上面推导 MSE 时类似, 我们可以得到的负对数似然实际上就是 MAE 损失的形式 \[ \begin{gathered} L(x, y)=\prod_{i=1}^N \frac{1}{2} \exp \left(-\left|y_i-\hat{y}_i\right|\right) \\ L L(x, y)=N \ln \frac{1}{2}-\sum_{i=1}^N\left|y_i-\hat{y}_i\right| \\ N L L(x, y)=\sum_{i=1}^N\left|y_i-\hat{y}_i\right| \end{gathered} \]

1.3 MAE 与 MSE 区别

MAE 和 MSE 作为损失函数的主要区别是:MSE 损失相比 MAE 通常可以更快地收敛,但 MAE 损失对于 outlier 更加健壮,即更加不易受到 outlier 影响。

  • MSE 通常比 MAE 可以更快地收敛。当使用梯度下降算法时, MSE 损失的梯度为 \(-\hat{y}_i\), 而 MAE 损失的梯度为 \(\pm 1\) , 即 MSE 的梯度的 scale 会随误差大小变化, 而 MAE 的梯度的 scale 则一直保持为 1 , 即便在绝对误 差 \(\left|y_i-\hat{y}_i\right|\) 很小的时候 MAE 的梯度 scale 也同样为 1 , 这实际上是非常不利于模型的训练的。当然你可以通 过在训练过程中动态调整学习率缓解这个问题, 但是总的来说, 损失函数梯度之间的差异导致了 MSE 在大部 分时候比 MAE 收敛地更快。这个也是 MSE 更为流行的原因。

  • MAE 对于异常值(outlier) 更加 robust。我们可以从两个角度来理解这一点:

    • 第一个角度是直观地理解,下图是 MAE 和 MSE 损失画到同一张图里面,由于MAE 损失与绝对误差之间是线性关系,MSE 损失与误差是平方关系,当误差非常大的时候,MSE 损失会远远大于 MAE 损失。因此当数据中出现一个误差非常大的 outlier 时,MSE 会产生一个非常大的损失,对模型的训练会产生较大的影响

      img
    • 第二个角度是从两个损失函数的假设出发,MSE 假设了误差服从高斯分布,MAE 假设了误差服从拉普拉斯分布。拉普拉斯分布本身对于 outlier 更加 robust。参考下图(来源:Machine Learning: A Probabilistic Perspective 2.4.3 The Laplace distribution Figure 2.8),当右图右侧出现了 outliers 时,拉普拉斯分布相比高斯分布受到的影响要小很多。因此以拉普拉斯分布为假设的 MAE 对 outlier 比高斯分布为假设的 MSE 更加 robust。img

1.4 Huber Loss

  • 在误差接近 0 时使用 MSE,使损失函数可导并且梯度更加稳定
  • 在误差较大时使用 MAE 可以降低 outlier 的影响,使训练对 outlier 更加健壮。

上文我们分别介绍了 MSE 和 MAE 损失以及各自的优缺点, MSE 损失收玫快但容易受 outlier 影响, MAE 对 outlier 更加健壮但是收玫慢, Huber LosS 则是一种将 MSE 与 MAE 结合起来, 取两者优点的损失函数, 也被称作 Smooth Mean Absolute Error Loss 。其原理很简单, 就是在误差接近 0 时使用 MSE, 误差较大时使用 MAE, 公 式为 \[ J_{\text {huber }}=\frac{1}{N} \sum_{i=1}^N \mathbb{I}_{\left|y_i-\hat{y_i}\right| \leq \delta} \frac{\left(y_i-\hat{y_i}\right)^2}{2}+\mathbb{I}_{\left|y_i-\hat{y}_i\right|>\delta}\left(\delta\left|y_i-\hat{y_i}\right|-\frac{1}{2} \delta^2\right) \] 上式中 \(\delta\) 是 Huber Loss 的一个超参数, \(\delta\) 的值是 MSE 和 MAE 两个损失连接的位置。上式等号右边第一项是 MSE 的部分, 第二项是 MAE 部分, 在 MAE 的部分公式为 \(\delta\left|y_i-\hat{y_i}\right|-\frac{1}{2} \delta^2\) 是为了保证误差 \(|y-\hat{y}|= \pm \delta\) 时 MAE 和 MSE 的取值一致,进而保证 Huber Loss 损失连续可导。

下图是 \(\delta=1.0\) 时的 Huber Loss, 可以看到在 \([-\delta, \delta]\) 的区间内实际上就是 MSE 损失, 在 \((-\infty, \delta)\)\((\delta, \infty)\) 区 间内为 MAE损失。

img

1.5 分位数损失 Quantile Loss

MAE 中分别用不同的系数控制高估和低估的损失,进而实现分位数回归

分位数回归 Quantile Regression 是一类在实际应用中非常有用的回归算法,通常的回归算法是拟合目标值的期望或者中位数,而分位数回归可以通过给定不同的分位点,拟合目标值的不同分位数

img

分位数回归是通过使用分位数损失 Quantile Loss 来实现这一点的, 分位数损失形式如下, 式中的 r 分位数系数\[ J_{\text {quant }}=\frac{1}{N} \sum_{i=1}^N \mathbb{I}_{\hat{y}_i \geq y_i}(1-r)\left|y_i-\hat{y_i}\right|+\mathbb{I}_{\hat{y}_i<y_i} r\left|y_i-\hat{y_i}\right| \] 我们如何理解这个损失函数呢? 这个损失函数是一个分段的函数, 将 \(\hat{y}_i \geq y_i \quad\) (高估) 和 \(\hat{y}_i<y_i \quad\) (低估) 两种 情况分开来, 并分别给予不同的系数。当 \(r>0.5\) 时, 低估的损失要比高估的损失更大, 反过来当 \(r<0.5\) 时, 高估的损失比低估的损失大; 分位数损失实现了分别用不同的系数控制高估和低估的损失, 进而实现分位数回归。 特别地, 当 \(r=0.5\) 时, 分位数损失退化为 MAE 损失, 从这里可以看出 MAE 损失实际上是分位数损失的一个特 例 一 中位数回归。

下图是取不同的分位点 \(0.2 、 0.5 、 0.6\) 得到的三个不同的分位损失函数的可视化,可以看到 0.2 和 0.6 在高估和低 估两种情况下损失是不同的, 而 0.5 实际上就是 MAE。

img

1.6 平均绝对百分误差 MAPE

虽然平均绝对误差能够获得一个评价值, 但是你并不知道这个值代表模型拟合是优还是劣, 只有通过对比才能达到 效果。当需要以相对的观点来衡量误差时, 则使用MAPE。 平均绝对百分误差(Mean Absolute Percentage Error, MAPE)是对 MAE 的一种改进, 考虑了绝对误差相对 真实值的比例。 - 优点:考虑了预测值与真实值的误差。考虑了误差与真实值之间的比例。 \[ M A P E=\frac{100}{m} \sum_{i=1}^m\left|\frac{y_i-f\left(x_i\right)}{y_i}\right| \] 在某些场景下, 如房价从 \(5 K\)\(50 K\) 之间, \(5 K\) 预测成 \(10 K\)\(50 K\) 预测成 \(45 K\) 的差别是非常大的, 而平均 绝对百分误差考虑到了这点。

一、聚类算法评价指标

https://zhuanlan.zhihu.com/p/343667804

十分钟掌握聚类算法的评估指标:https://juejin.cn/post/6997913127572471821

前言 【外部评估】+ 【内部指标】

如同之前介绍的其它算法模型一样,对于聚类来讲我们同样会通过一些评价指标来衡量聚类算法的优与劣。在聚类任务中,常见的评价指标有:纯度(Purity)兰德系数(Rand Index, RI)F值(F-score)调整兰德系数(Adjusted Rand Index,ARI)。同时,这四种评价指标也是聚类相关论文中出现得最多的评价方法。下面,我们就来对这些算法一一进行介绍。

img

好的聚类算法,一般要求类簇具有:

  • 簇内 (intra-cluster) 相似度高
  • 簇间 (inter-cluster) 相似度底

一般来说,评估聚类质量有两个标准,内部评估评价指标和外部评估指标。内部评估指标主要基于数据集的集合结构信息从紧致性、分离性、连通性和重叠度等方面对聚类划分进行评价。即基于数据聚类自身进行评估的。

1.1聚类纯度 - 聚类的准确率

在聚类结果的评估标准中, 一种最简单最直观的方法就是计算它的聚类纯度 (purity),别看纯度听起来很陌生, 但实际上和分类问题中的准确率有着异曲同工之妙因为聚类纯度的总体思想也用聚类正确的样本数除以总的样本 数, 因此它也经常被称为聚类的准确率。只是对于聚类后的结果我们并不知道每个簇所对应的真实类别, 因此需要 取每种情况下的最大值。具体的,纯度的计算公式定义如下: \[ P=(\Omega, \mathbb{C})=\frac{1}{N} \sum_k \max _j\left|\omega_k \cap c_j\right| \] 其中 \(N\) 表示总的样本数; \(\Omega=\left\{\omega_1, \omega_2, \ldots, \omega_K\right\}\) 表示一个个聚类后的簇, 而 \(\mathbb{C}=\left\{c_{1,2}, \ldots c_J\right\}\) 表示正确的类别; \(\omega_k\) 表示聚类后第 \(k\) 个簇中的所有样本, \(c_j\) 表示第 \(j\) 个类别中真实的样本。在这里 \(P\) 的取值范围为 \([0,1]\) ,越大表示 聚类效果越好。

1.2 兰德系数与F值 [同簇混淆矩阵]

在介绍完了纯度这一评价指标后,我们再来看看兰德系数(Rand Index)和F值。虽然兰德系数听起来是一个陌生 的名词, 但它的计算过程却也与准确率的计算过程类似。同时, 虽然这里也有一个叫做值的指标, 并且它的计算 过程也和分类指标中的F值类似, 但是两者却有着本质的差别。说了这么多, 那这两个指标到底该怎么算呢? 同分 类问题中的沘淆矩阵类似,这里我们也要先定义四种情况进行计数,然后再进行指标的计算。

为了说明兰德系数背后的思想,我们还是以图1中的聚类结果为例进行说明(为了方便观察,我们再放一张图在这 里):

img

  • \(T P\) : 表示两个同类样本点在同一个簇(布袋)中的情况数量;
  • \(F P\) : 表示两个非同类样本点在同一个簇中的情况数量;
  • \(T N\) : 表示两个非同类样本点分别在两个簇中的情况数量;
  • \(F N\) : 表示两个同类样本点分别在两个簇中的情况数量;

由此,我们便能得到如下所示的对混淆矩阵(Pair Confusion Matrix)

img

有了上面各种情况的统计值,我们就可以定义出兰德系数和F值的计算公式: \[ Precision =\frac{T P}{T P+F P} \]

\[ Recall =\frac{T P}{T P+F N} \]

\[ R I =\frac{T P+T N}{T P+F P+F N+T N} \]

\[ F_\beta =\left(1+\beta^2\right) \frac{\text { Precision } \cdot \text { Recall }}{\beta^2 \cdot \text { Precision }+ \text { Recall }} \]

从上面的计算公式来看, (3)(4) 从形式上看都非常像分类问题中的准确率与F值, 但是有着本质的却别。同时, 在 这里 \(R I\)\(F_\beta\) 的取值范围均为 \([0,1]\) ,越大表示聚类效果越好。

1.3 调整兰德系数(Adjusted Rand index)【归一化】

对于随机结果,RI并不能保证分数接近零。为了实现“在聚类结果随机产生的情况下,指标应该接近零”,调整兰德系数(Adjusted rand index)被提出,它具有更高的区分度。

其公式为: \[ \mathrm{ARI}=\frac{\mathrm{RI}-E[\mathrm{RI}]}{\max (\mathrm{RI})-E[\mathrm{RI}]} \] \(A R\) 取值范围为 \([-1,1]\), 值越大意味着聚类结果与真实情况越吻合。从广义的角度来讲, ARI衡量的是两个数据分布 的吻合程度。

优点:

  • 对任意数量的聚类中心和样本数, 随机聚类的ARI都非常接近于 0 。
  • 取值在 \([-1,1]\) 之间, 负数代表结果不好, 越接近于1越好。
  • 对簇的结构不需作出任何假设:可以用于比较聚类算法。

缺点:

  • ARI 需要 ground truth classes 的相关知识, ARI需要真实标签, 而在实践中几乎不可用, 或者需要人工 标注者 手动分配(如在监督学习环境中)。

1.4 标准化互信息(NMI, Normalized Mutual Information)

互信息是用来衡量两个数据分布的吻合程度。它也是一有用的信息度量,它是指两个事件集合之间的相关性。互信息越大,词条和类别的相关程度也越大。

1.5 轮廓系数(Silhouette Coefficient)

轮廓系数适用于实际类别信息末知的情况。对于单个样本, 设 \(a\) 是与它同类别中其他样本的平均距离, \(b\) 是与它距离最近不同类别中样本的平均距离, 其轮廓 系数为: \[ s=\frac{b-a}{\max (a, b)} \] 对于一个样本集合, 它的轮廓系数是所有样本轮廓系数的平均值。轮廓系数的取值范围是 \([-1,1]\), 同类别样本距离 越相近, 不同类别样本距离越远, 值越大。当值为负数时, 说明聚类效果很差。

1.6 Calinski-Harabaz指数(Calinski-Harabaz Index)

在真实的分群label不知道的情况下,Calinski-Harabasz可以作为评估模型的一个指标。

Calinski-Harabasz指数通过计算类中各点与类中心的距离平方和来度量类内的紧密度,通过计算各类中心点与数据集中心点距离平方和来度量数据集的分离度,CH指标由分离度与紧密度的比值得到。从而,CH越大代表着类自身越紧密,类与类之间越分散,即更优的聚类结果。

优点

  • 当簇的密集且分离较好时,分数更高。
  • 得分计算很快,与轮廓系数的对比,最大的优势:快!相差几百倍!毫秒级。

缺点

  • 凸的簇的CH指数通常高于其他类型的簇。例如,通过 DBSCAN 获得基于密度的簇;所以,不适合基于密度的聚类算法(DBSCAN)。

1.7 戴维森堡丁指数(DBI, Davies-Bouldin Index)

DB指数是计算任意两类别的类内距离平均距离之和除以两聚类中心距离求最大值。DB越小,意味着类内距 离越小同时类间距离越大。零是可能的最低值, 接近零的值表示更好的分区\[ \begin{gathered} R_{i j}=\frac{s_{i}+s_{j}}{d_{i j}} \\ D B=\frac{1}{k} \sum_{i=1}^{k} \max _{i \neq j} R_{i j} \end{gathered} \] 其中, \(s_{i}\) 表示簇的每个点与该簇的质心之间的平均距离, 也称为簇直径。 \(d_{i j}\) 表示聚类和的质心之间的距 离。 算法生成的聚类结果越是朝着簇内距离最小(类内相似性最大)和笶间距离最大(类间相似性最小)变化, 那么Davies-Bouldin指数就会越小。 缺点:

  • 因使用欧式距离, 所以对于环状分布聚类评测很差。

一、A/B 测试

【AB测试最全干货】史上最全知识点及常见面试题(上篇) - 数据分析狗一枚的文章 - 知乎 https://zhuanlan.zhihu.com/p/375902281

引言

科学家门捷列夫说「没有测量,就没有科学」,在AI场景下我们同样需要定量的数值化指标来指导我们更好地应用模型对数据进行学习和建模。

事实上,在机器学习领域,对模型的测量和评估至关重要。选择与问题相匹配的评估方法,能帮助我们快速准确地发现在模型选择和训练过程中出现的问题,进而对模型进行优化和迭代。本文我们系统地讲解一下机器学习模型评估相关知识。

1.1 模型评估的目标

模型评估的目标是选出泛化能力强的模型完成机器学习任务。实际的机器学习任务往往需要进行大量的实验,经过反复调参、使用多种模型算法(甚至多模型融合策略)来完成自己的机器学习问题,并观察哪种模型算法在什么样的参数下能够最好地完成任务。

但是我们无法提前获取「未知的样本」,因此我们会基于已有的数据进行切分来完成模型训练和评估,借助于切分出的数据进行评估,可以很好地判定模型状态(过拟合 or 欠拟合),进而迭代优化。

在建模过程中,为了获得泛化能力强的模型,我们需要一整套方法及评价指标。

  • 评估方法:为保证客观地评估模型,对数据集进行的有效划分实验方法。
  • 性能指标:量化地度量模型效果的指标。

1.2 离线与在线实验方法

进行评估的实验方法可以分为「离线」和「在线」两种。

离线实验方法:

离线评估中,经常使用准确率(Accuracy)、查准率(Precision)、召回率(Recall)、ROC、AUC、PRC等指标来评估模型。

模型评估通常指离线试验。原型设计(Prototyping)阶段及离线试验方法,包含以下几个过程:

  • 使用历史数据训练一个适合解决目标任务的一个或多个机器学习模型。
  • 对模型进行验证(Validation)与离线评估(Offline Evaluation)。
  • 通过评估指标选择一个较好的模型。
在线实验方法:

在线评估与离线评估所用的评价指标不同,一般使用一些商业评价指标,如用户生命周期值(Customer Lifetime value)、广告点击率(Click Through Rate)、用户流失率(Customer Churn Rate)等标。

除了离线评估之外,其实还有一种在线评估的实验方法。由于模型是在老的模型产生的数据上学习和验证的,而线上的数据与之前是不同的,因此离线评估并不完全代表线上的模型结果。因此我们需要在线评估,来验证模型的有效性。

\(A/B Test\) 是目前在线测试中最主要的方法。 \(A/B Test\) 是为同一个目标制定两个方案让一部分用户使用 \(A\) 方案, 另一部分用户使用 \(B\) 方案, 记录下用户的使用情况, 看哪个方案更符合设计目标。如果不做AB实验直接上 线新方案,新方案甚至可能会毁掉你的产品。

 1.3 模型离线评估后,为什么要进行ab测试?

  • 离线评估无法消除过拟合的影响,因此离线评估结果无法代替线上的评估效果
  • 离线评估过程中无法模拟线上的真实环境,例如数据丢失、样本反馈延迟
  • 线上的某些商业指标例如收益、留存等无法通过离线计算

1.4 如何进行线上ab测试?

进行ab测试的主要手段时对用户进行分桶,即将用户分成实验组和对照组。实验组使用新模型,对照组使用base模型。分桶过程中需要保证样本的独立性和采样的无偏性,确保每个用户只划分到一个桶中,分桶过程中需要保证user id是一个随机数,才能保证数据无偏的。

二、模型评估

2.1 holdout

留出法是机器学习中最常见的评估方法之一,它会从训练数据中保留出验证样本集,这部分数据不用于训练,而用于模型评估

2.2 交叉验证

留出法的数据划分,可能会带来偏差。在机器学习中,另外一种比较常见的评估方法是交叉验证法—— \(K\) 折交叉验证对\(K\) 个不同分组训练的结果进行平均来减少方差

2.3 自助法

Bootstrap 是一种用小样本估计总体值的一种非参数方法,在进化和生态学研究中应用十分广泛。Bootstrap通过有放回抽样生成大量的伪样本,通过对伪样本进行计算,获得统计量的分布,从而估计数据的整体分布

三、超参数调优

神经网咯是有许多超参数决定的,例如网络深度,学习率,正则等等。如何寻找最好的超参数组合,是一个老人靠经验,新人靠运气的任务。

3.1 网格搜索

3.2 随机搜索

3.3 贝叶斯优化

贝叶斯优化什么?【黑盒优化】

求助 gradient-free 的优化算法了,这类算法也很多了,贝叶斯优化就属于无梯度优化算法中的一种,它希望在尽可能少的试验情况下去尽可能获得优化命题的全局最优解。

img

  • 目标函数 \(f(x)\) 及其导数末知, 否则就可以用梯度下降等方法求解。
  • 计算目标函数时间成本大, 意味着像蚁群算法、遗传算法这种方法也失效了, 因为计算一次要花费很多时间。
概述

贝叶斯优化, 是一种使用贝叶斯定理来指导搜索以找到目标函数的最小值或最大值的方法, 就是在每次迭代的时 候, 利用之前观测到的历史信息 (先验知识) 来进行下一次优化, 通俗点讲, 就是在进行一次迭代的时候, 先回顾下之前的迭代结果, 结果太差的 \(x\) 附近就不去找了, 尽量往结果好一点的 \(x\) 附近去找最优解, 这样一来搜索的效率就大大提高了, 这其实和人的思维方式也有点像, 每次在学习中试错, 并且在下次的时候根据这些经验来找到最 优的策略。

贝叶斯优化过程

首先,假设有一个这样的函数\(c(x)\),我们需要找到他的最小值,如下图所示,这也是我们所需要优化的目标函数,但是我们并不能够知道他的具体形状以及表达形式是怎么样的。

img

贝叶斯优化是通过一种叫做代理优化的方式来进行的,就是不知道真实的目标函数长什么样,我们就用一个代理函数(surrogate function)来代替目标函数而这个代理函数就可以通过先采样几个点,再通过这几个点来给他拟合出来,如下图虚线所示:

img

基于构造的代理函数, 我们就可以在可能是最小值的点附近采集更多的点, 或者在还没有采样过的区域来采集更多 的点,有了更多点,就可以更新代理函数,使之更逼近真实的目标函数的形状,这样的话也更容易找到目标函数的 最小值, 这个采样的过程同样可以通过构建一个采集函数来表示, 也就是知道了当前代理函数的形状, 如何选择下 一个 \(x\) 使得收益最大。

然后重复以上过程,最终就可以找到函数的最小值点了,这大致就是贝叶斯优化的一个过程:

  1. 初始化一个代理函数的先验分布
  2. 选择数据点 \(x\), 使得采集函数 \(a(x)\) 取最大值
  3. 在目标函数 \(c(x)\) 中评估数据点 \(x\) 并获取其结果 \(y\)
  4. 使用新数据 \((x, y)\) 更新代理函数,得到一个后验分布 (作为下一步的先验分布)
  5. 重复2-4步,直到达到最大迭代次数

举个例子, 如图所示, 一开始只有两个点 \((\mathrm{t}=2)\), 代理函数的分布是紫色的区域那块, 然后根据代理函数算出一 个采集函数(绿色线), 取采集函数的最大值所在的 \(x\) (红色三角处), 算出 \(y\), 然后根据新的点 \((x, y)\) 更新 代理函数和采集函数 \((\mathrm{t}=3)\) ,继续重复上面步骤,选择新的采集函数最大值所在的 \(x\), 算出 \(y\), 再更新代理函 数和采集函数, 然后继续迭代。

img

问题的核心就在于代理函数和采集函数如何构建,常用的代理函数有:

  1. 高斯过程(Gaussian processes)
  2. Tree Parzer Estimator
  3. 概率随机森林:针对类别型变量

采集函数则需要兼顾两方面的性质:

  1. 利用当前已开发的区域(Exploitation):即在当前最小值附近继续搜索
  2. 探索尚未开发的区域(Exploration):即在还没有搜索过的区域里面搜索,可能那里才是全局最优解

常用的采集函数有:

  1. Probability of improvement(PI)
  2. Expected improvement(EI)
  3. Confidence bound criteria,包括LCB和UCB

3.4 Hyperopt

Hyperopt 是一个强大的 Python 库,用于超参数优化,由 jamesbergstra 开发。Hyperopt 使用贝叶斯优化的形式进行参数调整,允许你为给定模型获得最佳参数。它可以在大范围内优化具有数百个参数的模型。

参考文献

贝叶斯优化(原理+代码解读)

LightGBM调参指南(带贝叶斯优化代码)

  • 贝叶斯调参采用高斯过程,考虑之前的参数信息,不断地更新先验;网格搜索未考虑之前的参数信息
  • 贝叶斯调参迭代次数少,速度快;网格搜索速度慢,参数多时易导致维度爆炸
  • 贝叶斯调参针对非凸问题依然稳健;网格搜索针对非凸问题易得到局部最优

可用的贝叶斯优化框架

  1. BayesianOptimization:https://github.com/fmfn/BayesianOptimization
  2. 清华开源的openbox:https://open-box.readthedocs.io/zh_CN/latest/index.html
  3. 华为开源的HEBO:https://github.com/huawei-noah/HEBO
  4. Hyperopthttp://hyperopt.github.io/hype

Python-面试考点

  • 运行可视化网站: https://pythontutor.com/
  • 吐血总结!40道Python面试题集锦(附答案) - 幽默的程序猿日常的文章 - 知乎 https://zhuanlan.zhihu.com/p/366679675

1.基础知识

a.基础要求

  • 参数中的 * 和 的作用(要求:基础知识)**

    python 从参数定位到仅限关键字参数:https://blog.csdn.net/littleRpl/article/details/89457557

    参数中*以及**的作用:https://blog.csdn.net/weixin_41978699/article/details/121008512

    *参数:将任何剩余的参数都以元组的方式传入这个可变参数。允许省略可变参数的函数名

    **参数:会将所有参数放入一个dict供函数使用

  • 选代器(iterator)和生成器(generator) (要求:基础知识)

  • str和unicode的区别(python2下)(要求:基础知识)

  • 装饰器用途(要求:基础知识)

  • is和==的区别(要求:基础知识)

    • == 符号比较的是两个对象的值,is 比较的是两个的对象的标识【id()函数 , Cpython中内存地址】
    • x is None
    • is 符号比 == 运算快【不能重载】,== 是语法糖,【a.__ eq __(b)】
  • **双下划线开头的魔法方法,比如_add__(至少知道一个,以及其作用)**

  • 字符串编码:ascii/unicode/utf-8 分别是什么,以及三者的区别(要求:要求至少能回答一个)

  • 内置数据结构

    • dict (要求:至少知道底层实现是基于hash表,可以扩展提问)

    • list (要求:至少知道底层实现类似链表,可以扩展提问)

    • tuple(要求:知道其与list(异同)

      • 元组的相对不可变形:标识不变,值可变
    • set(要求:知道使用场景)

b.进阶要求

  • yield / yield from的区别(要求:可选)

yield只是将普通函数变成生成器,yield一个值,迭代时可以得到一个值;而yield from是将后面的值变成一个可迭代对象。

  • python 性能优化(要求:基础知识)

    • python为什么慢?

      • python是动态语言

      • python是解释执行

      • python中一切都是对象

      • python GIL

      • 垃圾回收

  • python 模块查找顺序(要求:发挥空间比较大,junior岗位至少知道基本知识)

  • python 字符串编码问题为什么这么复杂(要求:非必须,发挥空间较大,可以引导被面者深入牌答,以及与其他语言的对比)

2 正则表达式

a.基础要求

能够根据实际需求,实现中等难度的正则表达式

b.进阶要求

贪婪与非贫要模式(要求:非junior岗位需要掌握) 捕获与命名捕获(要求:非junior岗位需要掌握)

3 常见模块使用(待补充完善)

a.基础要求:

能够熟练应用基础python模块,如string,logging,json,collections(熟悉其中一个)

b.进阶要求:

1requests,itertools,multiprocessing等

==4.垃圾回收==

a.基础要求:

垃圾回收过程(要求:必须目产出引用计数,其他非必须,发挥里间级大,可以引导面试者深 入回答) 引用计数的缺点(要求:必須掌握)

b.进阶要求:

分线回收


PythonQ&A

https://blog.csdn.net/u013486414/article/details/119701505

代码效率优化?

  • 尽量使用python内置函数
  • 字符串拼接使用python的标准式
  • 需要单次遍历的迭代的数组采用生成器替代【惰性计算】
  • if x代替if x==True

什么是duck type?

鸭子类型更关注对象的行为,只要实现了某种接口方法就行,而不在乎是什么类型(比如说定义了 __iter__魔法方法的类实例对象都可以用for来迭代)

py3和py2的区别

  • print在py3里是一个函数,在py2里只是一个关键字
  • py3文件的默认编码是utf8,py2文件的默认编码是ascii
  • py3的str是unicode字符串,而py2的str是bytes
  • py3的range()返回一个可迭代对象,py2的 range()返回一个列表,xrange()返回一个可迭代对象,
  • py3的除法返回float,py2的除法返回int

可变对象与不可变对象

  • 可变对象: list,dict,set
  • 不可变对象: bool,int,float,tuple,str, frozenset

可哈希和不可哈希对象

什么时候需要捕获异常?

  • Django的ORM框架操作数据库时,获取数据,更新数据等都有可能会异常
  • socket通信时,recv()方法可能会因为对方突然中断连接导致异常

什么是CPython GIL?

GIL,Global Interpreter Lock,即全局解释器锁,引入GIL是因为CPython的内存管理并不是线程安全的,为了保护多线程下对python对象的访问,每个线程在执行过程中都需要先获取GIL,保证同一时刻只有一个线程在执行代码,GIL使得python的多线程不能充分发挥多核CPU的性能,对CPU密集型程序的影响较大。

什么是生成器?

生成器是一种可迭代对象,可以挂起并保持当前的状态

生成器遇到yield处会停止执行,调用next()或send()才会继续执行

定义一个生成器有两种方式,一种是生成器推导式,一种是在普通函数中添加yield语句并实例化

浅拷贝和深拷贝

浅拷贝出来的是一个独立的对象,但它的子对象还是原对象中的子对象

深拷贝会递归地拷贝原对象中的每一个子对象,因此拷贝后的对象和原对象互不相关。

迭代器与可迭代对象的区别

可迭代对象类,必须自定义__iter__()魔法方法,range,list类的实例化对象都是可迭代对象

迭代器类,必须自定义__iter__()和__next__()魔法方法,用iter()函数可以创建可迭代对象的迭代器

闭包

闭包就是一个嵌套函数,它的内部函数 使用 外部函数的变量或参数,它的外部函数返回了内部函数

可以保存外部函数内的变量,不会随着外部函数调用完而销毁。

python垃圾回收机制

引用计数为主,标记清除分代回收为辅

引用计数机制是这样的:

  • 当对象被创建,被引用,作为参数传递,存储到容器中,引用计数+1

  • 当对象离开作用域,引用指向别的对象,del,从容器中移除,引用计数-1

  • 当引用计数降为0,python就会自动回收该对象所在的内存空间,

  • 但是引用计数无法解决循环引用的问题,所以引入了标记清除分代回收机制

async和await的作用

async: 声明一个函数为异步函数,函数内只要有await就要声明为async

await: 搭配asyncio.sleep()时会切换协程,当切换回来后再继续执行下面的语句

内置的数据结构和算法

  • 内置数据结构: list,dict,tuple,set
  • 内置算法: sorted,max

collections模块

collections模块提供了一些好用的容器数据类型,其中常用的有: namedtuple,dequeCounterOrderedDict,defaultdict

为什么dict查找的时间复杂度是O(1)?

dict底层是哈希表,哈希表类似于C语言的数组,可以实现按索引随机访问

但dict的key不一定是整数,需要先通过哈希函数,再经过取余操作转换为索引

list tuple的底层结构

list和tuple底层都是顺序表结构

list底层是可变数组,数组里存放的是元素对象的指针

set的底层结构

哈希表,key就是元素,value都是空

class方法 和 static方法的区别

class方法的第一个参数是cls,可以访问类属性,类方法

static方法和普通函数一样,只不过是放在类里,要通过类或实例来调用,但是它不能访问类和实例的属性和方法

什么是装饰器?

装饰器是一个接收函数作为参数的闭包函数

它可以在不修改函数内部源代码的情况下,给函数添加额外的功能

1
2
3
4
5
6
7
8
9
import time

def calc_time(func):
def inner():
t1 = time.time()
func()
t2 = time.time()
print('cost time: {}s'.format(t2-t1))
return inner

什么是元类? 使用场景

元类是创建类的类type还有继承自type的类都是元类

作用: 在类定义时(new, init)和 类实例化时(call) 可以添加自定义的功能

使用场景: ORM框架中创建一个类就代表数据库中的一个表,但是定义这个类时为了统一需要把里面的类属性全部改为小写,这个时候就要用元类重写new方法,把attrs字典里的key转为小写

Python局部变量

python中list作为全局变量无需global声明的原因,则不会有歧义。它是“明确的”,因为如果把b当作是局部变量的话,它会报KeyError,所以它只能是引用全局的b,故不需要多此一举显式声明global。

Python 读取大文件?

最近无论是面试还是笔试,有一个高频问题始终阴魂不散,那就是给一个大文件,至少超过10g,在内存有限的情况下(低于2g),该以什么姿势读它?

一般:

  • with 上下文管理器会自动关闭打开的文件描述符
  • 在迭代文件对象时,内容是一行一行返回的,不会占用太多内存
1
2
3
4
5
6
7
8
def retrun_count(fname):
"""计算文件有多少行
"""
count = 0
with open(fname) as file:
for line in file:
count += 1
return count

更底层: fp.read() iter(partial(file.read, block_size), '')

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def chunked_file_reader(fp, block_size=1024 * 8):
"""生成器函数:分块读取文件内容
"""
while True:
chunk = fp.read(block_size)
# 当文件没有更多内容时,read 调用将会返回空字符串 ''
if not chunk:
break
yield chunk
# iter()优化
def chunked_file_reader(file, block_size=1024 * 8):
"""生成器函数:分块读取文件内容,使用 iter 函数
"""
# 首先使用 partial(fp.read, block_size) 构造一个新的无需参数的函数
# 循环将不断返回 fp.read(block_size) 调用结果,直到其为 '' 时终止
for chunk in iter(partial(file.read, block_size), ''):
yield chunk

def return_count_v3(fname):
count = 0
with open(fname) as fp:
for chunk in chunked_file_reader(fp):
count += 1
return count

pandas分批读取大数据集?

pandas 的 chunksize 读取

Python可变数据类型 不可变数据类型?

不可变类型:数值型、字符串型string和元组tuple;不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象。

可变数据类型:列表list和字典dict,Set集合