内含Scikit-learn的主要数据模型包括分类,回归,超参调优,评估方法。这真的是一个很优雅的框架。在使用Tensorflow和Pytorch的人都知道,sklearn的东西是可以直接拿来辅助使用的。
他们更多是一种统计模型,因为日常我们不可能总是训练神经网络。更多的是在更小的数据集上进行统计分析,所以这个框架是日常建模的最佳选择。这么说起来其实大模型和现在的神经网络模型我们更适合的是直接
from sklearn import linear_model
def linear_reg(data, labels):
reg = linear_model.LinearRegression()
reg.fit(data, labels)
return reg
优化算法是最小二乘法。目的是残差平方和的最小化。但是最小二乘法,依赖于特征之间互相独立,比如很多features都是描述货币的不同币种,那么就会产生很多噪声,因为他们之间不相互独立。
岭回归是一种正则化方法,通过引入一个正则项拉姆达,来降低噪声防过拟合。
普通的模型建模:
from sklearn import linear_model
reg = linear_model.Ridge(alpha=0.1)
reg.fit(data, prices)
print('Coefficients: {}\n'.format(repr(reg.coef_)))
print('Intercept: {}\n'.format(reg.intercept_))
r2 = reg.score(pizza_data, pizza_prices)
print('R2: {}\n'.format(r2))
这里的coef是系数,intercept是截距,都是表达自变量对因变量的影响程度。最后的R2是一项评估,表达模型可以解释的变异程度,预测准确程度。
下面是使用了交叉验证的岭回归模型:
from sklearn import linear_model
alphas = [0.1, 0.2, 0.3]
reg = linear_model.RidgeCV(alphas=alphas)
reg.fit(data, prices)
print('Coefficients: {}\n'.format(repr(reg.coef_)))
print('Intercept: {}\n'.format(reg.intercept_))
print('Chosen alpha: {}\n'.format(reg.alpha_))
最后的输出可以输出最好的alpha系数。
写成漂亮的函数:
def cv_ridge_reg(data, labels, alphas):
reg = linear_model.RidgeCV(alphas=alphas)
reg.fit(data, labels)
return reg
岭回归是一种L2正则化方法,那么Lasso回归就是L1正则化。L1正则又叫做稀疏正则化,面对特征值较多的情况使用稀疏正则化可以降低复杂度(权重系数为0),防止过拟合,提高模型泛化。但是不可否认一些副作用,有可能导致模型出现偏差,因为可能删掉重要的特征,导致模型精度下降。
另外MAE平均绝对误差的评价标准也叫L1Loss,MSE均方误差那么就是L2Loss了。(Pytorch学习时候想到。)
Lasso回归的代码:
# predefined dataset
print('Data shape: {}\n'.format(data.shape))
print('Labels shape: {}\n'.format(labels.shape))
from sklearn import linear_model
reg = linear_model.Lasso(alpha=0.1)
reg.fit(data, labels)
print('Coefficients: {}\n'.format(repr(reg.coef_)))
print('Intercept: {}\n'.format(reg.intercept_))
print('R2: {}\n'.format(reg.score(data, labels)))
输出如下内容:(删除了空行)
Data shape: (150, 4)
Labels shape: (150,)
Coefficients: array([ 0. , -0. , 0.40811896, 0. ])
Intercept: -0.5337110569441175
R2: 0.8958211202747038
可以看到四个维度的特征,好几个被0掉了。然后写成好看的函数:
def lasso_reg(data, labels, alpha):
reg = linear_model.Lasso(alpha=alpha)
reg.fit(data, labels)
return reg
首先还是要理解贝叶斯技术,还是让我想起以前一个讲解贝叶斯公式的视频。这个视频讲的很棒。对公式很清楚,但是公式背后讲的是什么。
# predefined dataset from previous chapter
print('Data shape: {}\n'.format(data.shape))
print('Labels shape: {}\n'.format(labels.shape))
from sklearn import linear_model
reg = linear_model.BayesianRidge()
reg.fit(data, labels)
print('Coefficients: {}\n'.format(repr(reg.coef_)))
print('Intercept: {}\n'.format(reg.intercept_))
print('R2: {}\n'.format(reg.score(data, labels)))
print('Alpha: {}\n'.format(reg.alpha_))
print('Lambda: {}\n'.format(reg.lambda_))
最后得出的阿尔法是控制先验分布形状的参数,阿尔法越大分布越趋进于0,拉姆达是控制先验分布尺度的参数,拉姆达越大,先验分布越分散。他们都是控制模型复杂度的重要参数。然后上函数:
def bayes_ridge(data, labels):
reg = linear_model.BayesianRidge()
reg.fit(data, labels)
return reg
贝叶斯技术适用哪些问题?回归问题,空间统计,时间序列分析,还有机器学习,但是他的计算量很大,对于量化不确定性是一种强大的统计工具。
名字容易混淆视听的一种模型,是一种分类模型,因为他输出的是logits是对结果的一种可能性预估,因为是连续空间的概率事件,所以也可以说是回归,但是本质是通过概率确定分类结果。
相关代码:
# predefined dataset
print('Data shape: {}\n'.format(data.shape))
# Binary labels
print('Labels:\n{}\n'.format(repr(labels)))
from sklearn import linear_model
reg = linear_model.LogisticRegression()
reg.fit(data, labels)
# create data for prediction
new_data = np.array([
[ 0.3, 0.5, -1.2, 1.4],
[ -1.3, 1.8, -0.6, -8.2]])
print('Prediction classes: {}\n'.format(
repr(reg.predict(new_data))))
上面是一个二分类问题,同时逻辑回归还可以用于多分类问题。只要对模型加入参数multi_class,这个参数的默认设置是ovr(意思是One-vs-Rest,将该类和其他所有的类别分开,将多分类问题转化为二分类问题的方法),下面使用不同的策略,多分类策略。
下面的例子假定有三个分类。
from sklearn import linear_model
reg = linear_model.LogisticRegression(
solver = 'lbfgs',
multi_class = 'multinomial',
max_iter = 200
)
reg.fit(data, labels)
# create data for prediction
new_data = np.array([
[ 0.3, 0.5, -1.2, 1.4],
[ -1.3, 1.8, -0.6, -8.2]])
print('Prediction classes: {}\n'.format(
repr(reg.predict(new_data))))
max_iter是训练轮次,默认是100,但是如果模型不能收敛,就会收到“收敛警告”,所以可以设置的大一点这里就设置了200,一般小的数据集用100到500,中型数据量用500到1000,再大就1000往上了。
关于solver到选择,根据需要正则化和数据规模进行选择,可以参考官网。
比如代码:
from sklearn import linear_model
reg = linear_model.LogisticRegression(solver='lbfgs', max_iter=1000)
最后总结成函数:
def multiclass_lr(data, labels, max_iter):
reg = linear_model.LogisticRegression(solver='lbfgs', max_iter=max_iter, multi_class='multinomial')
reg.fit(data, labels)
return reg
世界到处都是0和1,一个问题无法就是发生和不发生,阴阳,有无,都是如此。决定树就是这样!布尔的美妙。世界也是离散分布的,看似连续的值,其实在无比精细的每一个点其实也就是0和1的问题,所以强大的决定树可以解决分类也可以解决回归问题!
from sklearn import tree
clf_tree1 = tree.DecisionTreeClassifier()
reg_tree1 = tree.DecisionTreeRegressor()
clf_tree2 = tree.DecisionTreeClassifier(
max_depth=8) # max depth of 8
reg_tree2 = tree.DecisionTreeRegressor(
max_depth=5) # max depth of 5
# predefined dataset
print('Data shape: {}\n'.format(data.shape))
# Binary labels
print('Labels:\n{}\n'.format(repr(labels)))
clf_tree1.fit(data, labels)
在分类模型中,使用信息熵和基尼不纯度衡量每个节点的好坏,在回归模型中使用均方误差(MSE),平均绝对误差(MAE)等。
最常用的sklearn方法。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)
小函数:
def dataset_splitter(data, labels, test_size=0.25):
split_dataset = train_test_split(data, labels, test_size=test_size)
train_set = (split_dataset[0], split_dataset[2])
test_set = (split_dataset[1], split_dataset[3])
return (train_set, test_set)
注意这个方法内部会帮我们打乱数据。
使用sklearn可以很方便的实现交叉验证。当我们有很大的数据集,可以将数据分为training,validation,testing,三个数据集的时候,使用交叉验证方法来训练training,validation的不同组合,可以使得训练变的更加强大。
分类算法使用精确度accuracy作为最后的评价标准。
from sklearn import linear_model
from sklearn.model_selection import cross_val_score
clf = linear_model.LogisticRegression(max_iter=3000)
# Predefined data and labels
cv_score = cross_val_score(clf, data, labels, cv=3) # k = 3
回归算法使用R^2作为最后的评价标准。
from sklearn import linear_model
from sklearn.model_selection import cross_val_score
reg = linear_model.LinearRegression()
# Predefined data and labels
cv_score = cross_val_score(reg, data, labels, cv=4) # k = 4
注意到使用交叉验证的方法cross_val_score
我们不需要手动进行fit了,因为他内部会帮我们fit!
内部使用的算法是stratified K-Fold
算法,这个算法基本保证每个round中,0和1的标签数量比都相同。
解析下面的交叉验证代码:
is_clf = True # 假设这是一个分类算法的model
for depth in range(3, 8):
# 进行交叉验证输出一个scores的列表
scores = cv_decision_tree(
is_clf, data, labels, depth, 5) # k = 5
mean = scores.mean() # 算出平均精确度accuracy
std_2 = 2 * scores.std() # 两个标准区间,95%的置信区间
print('95% C.I. for depth {}: {} +/- {:.2f}\n'.format(
depth, mean, std_2))
最终就可以得到使用哪个参数可以得到更高的精确度,如果更大的超参数效果更好,那么可能需要继续扩大测试上线,使用8,9之类的。
下面是一个决策树使用交叉验证的函数:
def cv_decision_tree(is_clf, data, labels, max_depth, cv):
if is_clf:
d_tree = tree.DecisionTreeClassifier(max_depth=max_depth)
else:
d_tree = tree.DecisionTreeRegressor(max_depth=max_depth)
scores = cross_val_score(d_tree, data, labels, cv=cv)
return scores
回归一般是R方,均方误差,平均绝对误差,分类使用精确度或者混淆矩阵。
分类:
clf = tree.DecisionTreeClassifier()
clf.fit(train_data, train_labels)
predictions = clf.predict(test_data)
from sklearn import metrics
acc = metrics.accuracy_score(test_labels, predictions)
print('Accuracy: {}\n'.format(acc))
回归:
reg = tree.DecisionTreeRegressor()
reg.fit(train_data, train_labels)
predictions = reg.predict(test_data)
from sklearn import metrics
r2 = metrics.r2_score(test_labels, predictions)
print('R2: {}\n'.format(r2))
mse = metrics.mean_squared_error(test_labels, predictions)
print('MSE: {}\n'.format(mse))
mae = metrics.mean_absolute_error(test_labels, predictions)
print('MAE: {}\n'.format(mae))
如果数据集够小,可以详尽地进行超参数训练,那么可以使用GridSearch交叉验证的方法。
reg = linear_model.BayesianRidge()
params = {
'alpha_1':[0.1,0.2,0.3],
'alpha_2':[0.1,0.2,0.3]
}
reg_cv = GridSearchCV(reg, params, cv=5)
# predefined train and test sets
reg_cv.fit(train_data, train_labels)
print(reg_cv.best_params_)
注意到它和一般的交叉验证的方法不同。上面的普通交叉验证是将model和data一起作为参数,得到scores,这里的GridSearch是将所有的参数组合,以及model作为参数,将cv实例化,然后进行fit。
由于将所有的超参数进行了组合,所以在大的训练集上会非常慢,比较适合数据集不太大的情况。
注意,CV更多的是一种评分方法,也可以帮我们找到最好的超参数,但是GridSearch更多的是为了帮我们详尽地找到最好的参数。
万物都是向量,将任何X向量化,那么衡量他们之间的相似程度,可以用他们在空间中的余弦夹角来计算。越接近1,关系越近。
from sklearn.metrics.pairwise import cosine_similarity
data1 = np.array([
[ 1.1, 0.3],
[ 2.1, 0.6],
[-1.1, -0.4],
[ 0. , -3.2]])
data2 = np.array([
[ 1.7, 0.4],
[ 4.2, 1.25],
[-8.1, 1.2]])
cos_sims = cosine_similarity(data1, data2)
print(cos_sims)
将输出一个array表达的是两个数据中点的余弦相似度的值矩阵。
下面是一个实现,找到每一行中对相关数据的index的方法。
cos_sims = cosine_similarity(data)
# 填充对角线为0,排出了自己和自己的最大相似度
np.fill_diagonal(cos_sims, 0)
# 找到每一行的最大数的所在索引即可
similar_indexes = cos_sims.argmax(axis=1)
虽说KNN是一种有监督的算法,也就是空间中的点是有标签的,通过找到K个最邻近的点来投票,得出目标的分类。
但是也可以将它作为一种集合算法,找到距离目标点最近的K个点,返回这些点,和距离。
data = np.array([
[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[4.6, 3.1, 1.5, 0.2],])
from sklearn.neighbors import NearestNeighbors
nbrs = NearestNeighbors(n_neighbors=2)
nbrs.fit(data)
new_obs = np.array([
[5. , 3.5, 1.6, 0.3],
[4.8, 3.2, 1.5, 0.1]])
dists, knbrs = nbrs.kneighbors(new_obs)
# nearest neighbors indexes
print(f'{repr(knbrs)}\n')
# nearest neighbor distances
print(f'{repr(dists)}\n')
对空间中的点进行集群,找到K个集合,在K-means++算法中,一开始初始化的质心是随机的,通过不断更新和每个质心的距离,以及更新每个集群的质心的迭代操作,最终达到稳定状态的结果就是集群的结果。在大批量的数据上因为效率很低,所以有mini-batch-clustering的方法可以在一个小批量上进行集群,以提高效率和即时处理的效果。
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=3)
# data is predefined
kmeans.fit(data)
# cluster assignments
print(f'{repr(kmeans.labels_)}\n')
# centroids
print(f'{kmeans.cluster_centers}\n')
new_obs = np.array([
[5.1, 3.2, 1.7, 1.9],
[6.9, 3.2, 5.3, 2.2]])
# predict clusters
print(f'{repr(kmeans.predict(new_obs))}\n')
labels_
会给出每个点的标签,表示分到的第几组,如果是三个组可能就是0,1,2
的数组。cluster_centers
可以返回每个组的质心坐标。最后的predict
返回要预测的每个点的所属的集合的标签。
如果执行Mini-batch kmeans
:batch_size
参数是每次选取的样本数量。
from sklearn.cluster import MiniBatchKMeans
kmeans = MiniBatchKMeans(n_clusters=3, batch_size=10)
# predefined data
kmeans.fit(data)
# cluster assignments
print(f'{repr(kmeans.labels_)}\n')
# centroids
print(f'{repr(kmeans.cluster_centers_)}\n')
new_obs = np.array([
[5.1, 3.2, 1.7, 1.9],
[6.9, 3.2, 5.3, 2.2]])
# predict clusters
print(f'{repr(kmeans.predict(new_obs))}\n')
整合两种方法的函数:
def kmeans_clustering(data, n_clusters, batch_size):
if batch_size is None:
kmeans = KMeans(n_clusters=n_clusters)
else:
kmeans = MiniBatchKMeans(n_clusters=n_clusters, batch_size=batch_size)
kmeans.fit(data)
return kmeans
注意到Kmeans方法中,数据点都是围绕着质心成为一个球形。这是它的既定假设,但是这个假设不能包括所有的集合情况,因此使用kmeans方法就可能出现错误的结果,这是不可避免的。
而分层集合的算法,可以处理不同特性的数据。
分层聚类算法可以通过两种不同的方法来构建聚类层次结构:自底向上(自下而上)和自顶向下(自上而下)。
方法一:分裂(divisive)自底向上(自下而上):算法从单个数据点开始,并逐步将相似的数据点合并成更大的聚类,直到达到指定的停止条件为止。具体步骤如下:
方法二:聚集(agglomerative)自顶向下(自上而下):算法从整个数据集开始,并逐步将其划分成更小的子集,直到每个子集都满足某种终止条件。具体步骤如下:
区别:
其中聚合,自顶向下的方法更常用,下面是sklearn库代码,个人感觉是因为自顶向下使用的资源和步骤更小,如果是自底向上,那么每一个sample一开始都是一个类,每次合并都需要大量的计算,而自顶向下效率较高,并可以更快地到达需要的目标群组数量,当然我觉得如果目标群组数量本来就很多,甚至趋近于样本大小,那么自底向上也许就需要采纳了。灵活运用。
from sklearn.cluster import AgglomerativeClustering
agg = AgglomerativeClustering(n_clusters=3)
# predefined data
agg.fit(data)
# cluster assignments
print(f'{repr(agg.labels_)}\n')
刚刚的两个聚类算法都是我们给定了要几个类的情况,那么总有我们不知道该分几个类的情况,mean-shift算法就是帮我们决定分几个类的。
Mean Shift 聚类是一种无参数的密度估计和聚类算法。它通过在数据空间中移动一组数据点来寻找数据的密度最大值(模式),从而发现聚类中心。它的工作原理类似于将数据点视为粒子,然后让它们沿着密度梯度方向移动,直到到达密度的最大值。
在工作原理上,首先它为每个点选择一个随机初始点作为中心。然后对于每个候选中心,计算它周围点的密度估计,常用的密度估计方法是通过核函数(如高斯核函数)对数据点之间的距离进行加权计算。对于每个候选聚类中心,将它沿着密度梯度方向移动到密度估计增加的方向,直到达到局部密度的最大值。重复上述移动步骤,直到所有候选聚类中心不再改变位置或达到收敛条件。将最终的候选聚类中心作为聚类中心,将数据点分配到最近的聚类中心。
和梯度下降算法有异曲同工之妙。
它的优点是不需要预先指定聚类数量,而是根据数据的密度分布自动发现聚类中心。不受数据点分布的稀疏性或密集性的影响,对于不规则形状的聚类可以表现良好。与一些传统的聚类算法相比,Mean Shift 聚类在高维数据上也能够表现较好。
但是它的计算复杂度通常较高,特别是在处理大规模数据集时。同时算法涉及到的参数,如核函数的带宽,可能需要进行调参来获得最佳的聚类效果。另外初始的候选聚类中心的选择可能会影响最终的聚类结果,因此需要进行一定的初始化策略。
Mean Shift 聚类算法在许多领域都有广泛的应用,特别是在计算机视觉、图像分割、物体跟踪等领域。
代码实现和之前的kmean很相似,感谢sklearn的封装。
from sklearn.cluster import MeanShift
mean_shift = MeanShift()
# predefined data
mean_shift.fit(data)
# cluster assignments
print(f'{repr(mean_shift.labels_)}\n')
# centroids
print(f'{repr(mean_shift.cluster_centers_)}\n')
new_obs = np.array([
[5.1, 3.2, 1.7, 1.9],
[6.9, 3.2, 5.3, 2.2]])
# predict clusters
print(f'{repr(mean_shift.predict(new_obs))}\n')
这也是一个根据密度进行聚合分类的算法。
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,它能够发现具有足够高密度的区域,并将这些区域视为聚类,并且能够在噪声点周围识别出不属于任何聚类的孤立点。不要被DB两个字迷惑成数据库。
几个关键的概念:
sklearn的相关代码:
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=1.2, min_samples=30)
# predefined data
dbscan.fit(data)
# cluster assignments
print(f'{repr(dbscan.labels_)}\n')
# core samples
print(f'{repr(dbscan.core_sample_indices_)}\n')
num_core_samples = len(dbscan.core_sample_indices_)
print(f'Num core samples: {num_core_samples}\n')
这两个指标用法很相似,ARI是调整后的兰德指数(adjusted Rand index),AMI是调整后的互信息(adjusted mutual information)。
他们用来衡量两个聚类结果的相似度,标准在 -1 到 1 之间。0 代表具有随机性,-1 代表完全不相似,1 代表完美分类。并且它具有对称性,就是说作为参数的时候,真实标签和预测标签的顺序没所谓,得到的结果是一样的。
另外即使改变了标签的名称,也不会影响结果,比如[0, 0, 0, 1, 1, 1]和[1, 1, 1, 3, 3, 3]这两个就是完美分类,结果应该是1。
在sklearn中有相对应的metric可用。
from sklearn.metrics import adjusted_rand_score
from sklearn.metrics import adjusted_mutual_info_score
true_labels = np.array([0, 0, 0, 1, 1, 1])
pred_labels = np.array([0, 0, 1, 1, 2, 2])
ari = adjusted_rand_score(true_labels, pred_labels)
ami = adjusted_mutual_info_score(true_labels, pred_labels)
聚类算法还可以进行将维。使用特征聚类,可以将相似的特征归为一类达到降维的目的。属于数据处理的一部分了。
from sklearn.cluster import FeatureAgglomeration
agg = FeatureAgglomeration(n_clusters=2)
new_data = agg.fit_transform(data)