目录
- 最大似然估计法
- SVD、QR矩阵分解
- 梯度下降法求解参数
1. 梯度下降法
梯度下降法公式:
x
k
+
1
x_{k+1}
xk+1 =
x
k
x_k
xk -
α
\alpha
α *
(
Δ
x
k
Δ
y
k
)
\left(\frac{{\Delta}x_k}{{\Delta}y_k}\right)
(ΔykΔxk)
就是为了求函数的最小值点,我们先求取xmin,然后带入到函数中,求取整个函数的最小值f(xmin)
学习率
α
\alpha
α太大了容易不收敛,太小了迭代的比较慢。
常用来求解无约束情况下凸函数的极小值,是一种迭代类型的算法,因为凸函数只有一个极值点,因此求解出来的极小值点就是整个函数的最小值点。
- 批量梯度下降:使用所有样本在当前点的梯度值来对变量参数进行更新操作。
- 随机梯度下降:在更新变量参数的时候,选取一个样本的梯度来更新参数。
- 小批量梯度下降:结合BGD和SGD的特性,从原始数据中,每次选择n个样本来更新参数值,一般n选择10;
BGD、SGD、MBGD的区别:
- 当样本量为m的时候,每次迭代BGD算法中对于参数值更新一次,SGD算法中对于参数值更新m次,MBGD算法中对于参数值更新M/N次,相对来讲SGD算法的更新速度最快;
- SGD中对每个算法都会更新参数值,当样本值不太正常的时候,就有可能会导致本次参数更新产生相反的结果,即SGD的结果并不一定收敛,而是在收敛结果附近波动;
- SGD算是每一个样本会更新一次,所以SGD算法非常适合样本量比较大的情况以及在线机器学习(Online ML)
from matplotlib import pyplot as plt
learn_rate = 0.8
tol = 1e-5
iter_num = 0
X = []
Y = []
x0 = 3.1
f_change = f(x0)
f_current = f(x0)
X.append(x0)
Y.append(f_current)
# 原函数
def f(x):
return x**2
# 导函数
def f_(x):
return 2 * x
while f_change > tol:
x = x0 - learn_rate * f_(x0)
X.append(x)
Y.append(f(x))
f_change = abs(f(x) - f(x0))
x0 = x
# 画图
plt.figure(figsize=(8,4))
x_ = [-3 + 0.1 * _ for _ in range(60)]
y_ = [f(x) for x in x_]
plt.plot(X, Y, 'ro-')
plt.plot(x_, y_, color='green', linewidth=2)
plt.show()
2. 机器学习的定义
- 机器学习 是一门从数据研究算法的科学学科;
- 机器学习 根据已有的数据,进行算法选择,并基于算法和数据构建模型,最终对未来进行预测;
- 机器学习的理性认识
- 基本概念:输入、输出、假设函数,机器学习无法得到一个十分完美的假设函数
- 算法(T):根据业务需要和数据特征选择的相关算法,也就是一个数学公式;模型(E):基于数据和算法构建出来的模型;评估/测试§:对模型评估的策略
- 机器学习是人工智能的一个分支。我们使用计算机设计一个系统,使它能能够根据提供的训练数据按照一定的方式来学习;随着训练次数的不断增加,该系统可以在性能上不断学习和改进;通过参数优化的学习模型,能够预测相关问题的数据。
- 一些概念
名称 | 现象 |
---|---|
拟合 | 构建的算法能否很好的描述给定数据的特征 |
鲁棒性 | 就算存在异常数据,算法也能够较高的取拟合,实际生产中需要来过滤异常值 |
过拟合 | 算法过于复杂,太“符合数据样本的特征”,对实际生产中的数据特征无法拟合 |
欠拟合 | 算法不太符合样本的数据特征 |
3. 机器学习常见的应用框架
- scikit-learn
- 官网:[https://scikit-learn.org/]
- tensorflow
- Mahout(用的比较少,执行速度比较慢)
- Spark MLlib
4. 机器学习的商业场景
- 个性化推荐:个性化指的是根据各种因素来改变用户体验和呈现给用户内容,这些因素包含 用户的行为数据和上下文数据;推荐系统的目的是 像用户展现一个用户可能会感兴趣的商品列表。
- 精准营销:从用户群众中找出特定要求的营销对象;
- 客户细分/用户画像系统:试图将用户群组划分为不同的组,根据给定的用户特征进行客户分组。
- 预测建模以及分析:根据已有的数据进行建模,并使用得到的模型来预测未来。
5.机器学习的分类
5.1 根据时候有标签
- 有监督学习:用已知某种或者某些特性的样本作为训练集,已建立一个数学模型,在用已经建立的模型来预测未知样本。有监督学习是从标签化训练数据集中推断出模型的机器学习任务;
- 无监督学习:与监督学习相比,无监督学习的训练集中没有人为标注的结果,在非监督学习过程中,数据并不被特别标识,学习模型是为了推断出数据的一些内在结构。
- 半监督学习:考虑如何利用少量的标注样本和大量的未标注样本进行训练和分类的问题,是有监督学习和无监督学习的结合。
5.2 根据应用场景
分类 | 应用 | 是否是监督 |
---|---|---|
分类 | 将样本数据集中的样本映射到某个给定的类别中 | 有监督,y是离散的 |
聚类 | 将样本数据集中的样本分为几个类别属于同一个类别的样本相似性比较大 | 无监督 |
回归 | 反应了样本数据集中样本属性值的特性,通过函数表达样本 映射关系来发信啊属性值之间的依赖关系 | 有监督,y是连续的 |
关联规则 | 获取隐藏在数据项之间的关联或者相互关系,可以根据一个数据项的出现导出其他数据项的出现频率 |
5.3 机器学习、深度学习、人工智能的关系
- 机器学习是人工智能的一个子类;
- 深度学习是机器学习的一个子类,深度学习是基于传统的神经网络发展到多隐层的一种算法体现。
6 机器学习的开发流程
- 数据收集
- 数据预处理
- 特征提取
- 模型构建
- 模型测试评估
- 投入使用(模型的部署与整合)
- 迭代使用
6.1 数据的收集
- 数据的来源
- 用户访问行为数据
- 业务数据
- 外部的第三方数据
- 数据的存储
- 需要存储的数据:原始始数据、预处理后的数据、模型的结果
- 存储设施: mysql,HDFS,Hbase,Solr,Elasticsearch,Kafka,redis等
- 数据收集方式
- flume & Kafka
6.2 数据预处理–数据清洗和转换
- 实际生产环境中机器学习比较耗时的一部分;
- 大部分的机器学习模型所处理的都是特征,特征通常是输入变量所对应的可以用于模型的数值表示;
- 大部分情况下,手机到的数据需要经过预处理才能被算法所应用,预处理的操作主要包括以下几个部分:
- 数据过滤
- 处理数据缺失
- 处理可能的异常、错误或者异常值
- 合并多个数据源数据
- 数据汇总
- 对数据进行初步的预处理,需要将其转换为机器学习模型的表示形式,对于许多模型类型来说,这种表示就是包含数值数据的向量或者矩阵。
- 将类别数据编码成对应的数值表示(一般使用1-of-k的方法)
- 从文本数据提取有用的数据(一般使用词袋法或者TF-IDF)
- 处理图像或者音频数据(像素、声波、音振、振幅等)
- 数值数据转换为类别数据以减少变量的值,比如年龄分段
- 对数值数据进行转换,比如对数转换
- 对特征进行正则化、标准化,以保证统一模型的不同输入变量的取值范围相同
- 对现有变量进行组合或者转换已生成新的特征,比如平均数(做虚拟变量)不断尝试
- 类型特征转换
- 目的:将非数值的特征值转换为数值型的数据
- 转化方法:labelencoder 和 1-of-k(也称为哑编码,OneHotEncoder)
- 哑编码:假设变量的取值有K个,如果对这些值用1到K进行编码,则可以用维度为k的一个向量来表示变量的值。在这样的变量里,该取值所对应的的序号所在的元素为1,其他元素均为0.比如A,B,C, 其中A=[1,0,0],B=[0,1,0],C=[0,0,1]
- 文本数据抽取
- 从文本中抽取特征,将文本转换为模型可用的数值型特征
- 两种方法:词袋法和TF-IDF
- 词袋法:那么文本中出现的词条以及出现的次数就可以提现文档的特征;
- TF-IDF:词条的重要性随着它在文件中出现的次数成正比地增加,但同时也随着它在语料库中出现的频率成反比地下降。
- TF:某个词条在文件中出现的次数,TF=该词条数/文档中所有的词条数
- IDF:逆文档频率,指的是一个词条重要性的度量,一般计算方式为:语料库中总文件的数量/包含该词条的文档数,由于这个值可能非常大,一般会取对数。
- TF-IDF = TF*IDF
from collections import Counter, defaultdict
import math
# Counter 主要用于计数,有两个很重要的方法:obj.elements(),obj.most_common(2),
# 增加元素obj.update(['22','55']), 减少元素:obj.subtract(['22','55'])
document1 = 'DDACBADDCDDCEDDD'
document2 = 'BDDBDDADDCBDBDDBDC'
counter1 = Counter(document1)
counter2 = Counter(document2)
all_words = list(set(counter1.keys()) | set(counter2.keys()))
all_words.sort()
# 词袋法表示
bg_d1 = [counter1[_] for _ in all_words]
bg_d2 = [counter2[_] for _ in all_words]
# 用TF-IDF进行表示学习
TF_d1 = defaultdict(lambda: 0)
TF_d2 = defaultdict(lambda: 0)
IDF_dict = defaultdict(lambda: 0)
for word in all_words:
count = 0
# 要对词袋法进行归一化
TF_d1[word] = counter1[word]/len(document1)
TF_d2[word] = counter2[word]/len(document2)
for document in [document1, document2]:
if word in document:
count += 1
# 没有用对数,因为文本数量也不多
IDF = 2/count
IDF_dict[word] = IDF
tf_idf_d1 = [TF_d1[_] * IDF_dict[_] for _ in all_words]
tf_idf_d2 = [TF_d2[_] * IDF_dict[_] for _ in all_words]
tf_idf_d1, tf_idf_d2
模型的训练以及测试
- 模型的选择:对特定任务最优建模方法的选择和对特定模型最佳参数的选择。
在训练数据集上选择模型,并在测试数据集上测试模型的效果。迭代进行数据模型的修改,这种方式称为交叉验证
- 模型的选择会尽可能多的选择算法进行执行,并计较执行结果;
- 模型的测试一般以一下几个方面来比较,分别是准确率/召回率/精准率/F值
评估维度 | 指标 | 计算方法 |
---|---|---|
准确率 | Accuracy | 提取出的正确样本数/总的样本数 |
召回率 | Recall | 正确样本数/样本中的正例样本数 |
精准率 | Precision | 正确的正例样本数/预测为正例的样本数 |
F值 | F-score | 精确率和召回率的调和平均数 |
roc | … | 纵轴是TPR,横轴是FPR |
auc | … | roc 下面的面积 |
- 召回率越高,精确率也就越低,是互斥的【从判断正负样本的概率来来理解】,所以需要一个调和平均数来做平均。
- 扩展一下就是 混淆矩阵。
- ROC曲线中,TPR是纵轴,FPR是横轴;TPR增长的越快,曲线越往上屈,AUC就越大,反映了模型的分类性能就越好【正负样本不平衡时,评估方式比一般的精确度评价方式要好。】
回归模型的结果度量
- explained_variance_score:可解释方差的回归评分函数
- mean_absolute_error: 平均绝对误差
- mean_squared_error: 平方平均误差
# sklearn 中api使用
import numpy as np
from sklearn import metrics
y = np.array([1, 1, 2, 2])
pre_score = np.array([0.1, 0.4, 0.35, 0.8])
# 表示预测为该样本的概率,0.1表示预测为2个概率
# pos_label 表示正样本为2
fpr, tpr, thresholds = metrics.roc_curve(y, pre_score, pos_label=2)
print('fpr:', fpr)
print('tpr:', tpr)
print('thresholds', thresholds)
metrics.auc(fpr, tpr)
- 代码解释
y就是标准值,scores 是每个预测值对应的阳性概率,比如0.1就是指第一个数预测为阳性的概率为0.1,很显然,y 和 socres应该有相同多的元素,都等于样本数。pos_label=2 是指在y中标签为2的是标准阳性标签,其余值是阴性。
所以在标准值y中,阳性有2个,后两个;阴性有2个,前两个。
接下来选取一个阈值计算TPR/FPR,阈值的选取规则是在scores值中从大到小的以此选取,于是第一个选取的阈值是0.8
scores中大于阈值的就是预测为阳性,小于的预测为阴性。所以预测的值设为y_=(0,0,0,1),0代表预测为阴性,1代表预测为阳性。可以看出,真阴性都被预测为阴性,真阳性有一个预测为假阴性了。
FPR = FP / (FP+TN) = 0 / 0 + 2 = 0
TPR = TP/ (TP + FN) = 1 / 1 + 1 = 0.5
thresholds = 0.8
- 自己实现
def roc_exercise(y, pre_score, pos_label):
from matplotlib import pyplot as plt
# 取出负标签,没必要
neg_label = tuple(set(y) - {pos_label})[0]
# 先排序,然后逆序输出
pre_score.sort()
thresholds = pre_score[::-1]
fpr_list = []
tpr_list = []
for thre in thresholds:
TP_count = 0
FP_count = 0
FN_count = 0
TN_count = 0
fpr = 0
tpr = 0
pre_list = []
for score in pre_score:
if score >= thre :
pre_list.append(pos_label)
else:
pre_list.append(neg_label)
for i in range(len(pre_list)):
if y[i] == pre_list[i]:
if pre_list[i] == pos_label:
TP_count += 1
else:
TN_count += 1
else:
if pre_list[i] == pos_label:
FP_count += 1
else:
FN_count += 1
tpr = TP_count / (TP_count + FN_count)
fpr = FP_count / (FP_count + TN_count)
tpr_list.append(tpr)
fpr_list.append(fpr)
print('thresholds', thresholds)
print('fpr_list', fpr_list)
print('tpr_list', tpr_list)
plt.plot(fpr_list, tpr_list, color='b')
plt.grid(True)
plt.title('ROC cureve')
plt.show()
return None
# execute
y = np.array([1, 1, 2, 2, 2])
pre_score = np.array([0.1, 0.4, 0.35, 0.8, 0.6])
pos_label = 2
roc_exercise(y, pre_score, pos_label)
- 小练习
import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle, count
from sklearn import svm, datasets
from sklearn.metrics import roc_curve, auc
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import label_binarize
from sklearn.multiclass import OneVsRestClassifier
from scipy import interp
# load dataset
iris = datasets.load_iris()
x = iris['data']
y = iris['target']
# binarize the output
y = label_binarize(y, classes=[0, 1, 2])
n_classes = y.shape[1]
# add noise feature to make the problem harder
rs = np.random.RandomState(20)
n_samples, n_features =x.shape
X = np.c_[x, rs.randn(n_samples, 200 * n_features)]
# shuffle and split training and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.7)
# fit and predict each class
svc = svm.SVC(kernel='linear', probability=True)
classifer = OneVsRestClassifier(svc)
model = classifer.fit(X_train, y_train)
y_score = model.decision_function(X_test)
# Compute roc curve and roc area for each class
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
fpr[i], tpr[i], _ = roc_curve(y_test[:, i], y_score[:, i])
roc_auc[i] = auc(fpr[i], tpr[i])
lw = 2
plt.plot(fpr[0], tpr[0], color='darkorange', lw=lw,
label='label{0}, auc={1}'.format(0, roc_auc[0]))
plt.plot(fpr[1], tpr[1], color='r', lw=lw,
label='label{0}, auc={1}'.format(1, roc_auc[1]))
plt.plot(fpr[2], tpr[2], color='b', lw=lw,
label='label{0}, auc={1}'.format(2, roc_auc[2]))
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()
7 模型的部署和整合
- 当模型构建好之后,将训练好的模型存储到数据库中,方便其他使用模型的应用来加载(构建好的模型一般为一个矩阵)
- 模型需要周期性
- 一个月或者一周
8 模型的监控与反馈
- 当模型一旦投入到实际的生产环境中,模型的效果监控是非常重要的,往往需要关注业务效果和用户体验,所以有时候会进行A/B测试;
- 模型需要对用户的反馈进行相应操作,即进行模型整改,但是要注意异常反馈信号对模型的影响,故需要进行必要的数据预处理操作。
额外知识点–最大似然估计
最大似然估计
- 最大似然估计感性理解:当样本序列出现的可能性最大的时候(似然函数最大)对应的概率参数就是最优的模型参数。因为只有当这种情况下的时候,这个序列才有可能被抽取出来。如果当前序列出现的可能性最大的时候,所对应的模型参数不是最优可能的模型参数 θ \theta θ,那么模型参数取最优解 θ \theta θ的时候,样本序列出现的概率不是最大,就不会出现。
- 求解使得出现的样本序列概率最大的模型参数的过程就是最大似然估计(MLE)
- 求救步骤:
- 求出序列出现的似然函数f(x, θ \theta θ),一般是联合概率密度函数。
- 求解对数似然函数;
- 求出使得对数似然函数f(x, θ \theta θ)最大的 θ \theta θ;
- 求解似然函数方程;
from collections import defaultdict, Counter
import math
# demo
x = [1, 2, 2, 2, 2, 1, 2, 2, 3, 1, 3]
counter = Counter(x)
def p_1(x):
return 0.5 * x
def p_2(x):
return 0.3 + 0.4 * x
def p_3(x):
return 0.7 - 0.9 * x
# 写出似然函数
def like_function(x, y):
if x == 1:
return math.pow(p_1(x), counter(y))
elif x == 2:
return math.pow(p_2(x), counter(y))
else:
return math.pow(p_3(x), counter(y))
l_a = 1
for _ in x:
l_a *= like_function(x, _)
- 手撸极限思想
# 极限的思想
def f_x(x):
return x*x
x0 = 2
delta_x = 0.1
max_iter = 100
count = 1
y_change = 10
y_d_0 = 10
tol = 1e-10
while (count < max_iter) and (y_change > tol):
delta_x = delta_x / count
delta_y = f_x(x0 + delta_x) - f_x(x0)
y_ = delta_y / delta_x
y_change = y_d_0 - y_
y_d_0 = y_
count += 1
额外知识点–梯度下降法
import numpy as np
from matplotlib import pyplot as plt
import random
def get_data():
nrg = np.random.RandomState(seed=20)
n_sample = 100000
b_values = nrg.normal(loc=-1, scale=10, size=n_sample)
c_values = nrg.normal(loc=0, scale=1.0, size=n_sample)
is_show = False
if is_show:
plt.figure()
plt.subplot(1, 2, 1)
plt.hist(b_values, 100, color='r')
plt.subplot(1, 2, 2)
plt.hist(b_values, 100, color='g')
plt.suptitle('data vision', fontsize=20)
plt.show()
return b_values, c_values
def f(x, b, c):
return x**2 + b * x + c
def h(x, b, c):
return 2 * x + b
def gradient_descent(max_iter=1000, tol=1e-5, alpha=0.01, batch_size=50):
b_values, c_values = get_data()
# 在更新梯度的时候,看用多少样本
def H(x, b_values, c_values, batch_size):
"""
batch_size 表示批次大小,默认全部样本,为BGD
"""
if batch_size is None:
batch_size = len(b_values)
batch_index = random.sample(range(len(b_values)), batch_size)
grad_sum = 0
for i in batch_index:
grad_sum += h(x, b_values[i], c_values[i])
return grad_sum / batch_size
# 用全量样本计算函数值
def F(x0, b_values, c_values):
sum_f = 0
for b, c in zip(b_values, c_values):
sum_f += f(x0, b, c)
return sum_f
step = 0
b0 = b_values[0]
c0 = c_values[0]
x0 = -5
y0 = f(x0, b0, c0)
y_change = 10
step_list = []
y_list = []
y_change_list = []
while (step < max_iter) and (y_change > tol):
# cal gradient
x = x0 - alpha * H(x0, b_values, c_values, batch_size)
# cal y
y = F(x0, b_values, c_values)
# cal y_change
y_change = abs(y - y0)
x0 = x
y0 = y
step += 1
step_list.append(step)
y_list.append(y)
y_change_list.append(y_change)
plt.figure(figsize=(16, 10))
plt.subplot(1, 2, 1)
plt.plot(step_list, y_list, color='r')
plt.title('function values')
plt.grid(True)
plt.subplot(1, 2, 2)
plt.plot(step_list, y_change_list, color='g')
plt.title('loss values')
plt.grid(True)
plt.suptitle('one_sample')
plt.show()
gradient_descent(max_iter=1000, tol=1e-5, alpha=0.01, batch_size=1)