案例: 事件预测
加载event.txt, 预测某个时间段是否会出现特殊时间.
import numpy as np
import sklearn.preprocessing as sp
from sklearn import svm
import sklearn.model_selection as ms
class DigitEncoder ():
def fit_transform(self, x):
return x.astype(int)
def transform(self, x):
return x.astype(int)
def inverse_transform(self, x):
return x.astype(str)
data = []
with open('event.txt', 'r') as f:
for line in f.readlines():
data.append(line[:-1].split(','))
data = np.delete(np.array(data).T, 1, 0)
print(data)
encoders, x = [], []
for row in range(len(data)):
if data[row, 0].isdigit():
encoder = DigitEncoder()
else:
encoder = sp.LabelEncoder()
if row < len(data) - 1:
x.append(encoder.fit_transform(data[row]))
else:
y = encoder.fit_transform(data[row])
encoders.append(encoder)
x = np.array(x).T
train_x, test_x, train_y, test_y = ms.train_test_split(
x, y, test_size=0.25, random_state=5)
model = svm.SVC(kernel='rbf', class_weight='balanced')
model.fit(train_x, train_y)
pred_test_y = model.predict(test_x)
print((pred_test_y == test_y).sum() / test_y.size)
data = [['Tuesday', '14:00:00', '13', '11']]
data = np.array(data).T
x = []
for row in range(len(data)):
encoder = encoders[row]
x.append(encoder.transform(data[row]))
x = np.array(x).T
pred_y = model.predict(x)
print(encoders[-1].inverse_transform(pred_y))
案例交通流量预测
加载traffic.txt, 预测某个时间段某个路口的车流量.
import numpy as np
import sklearn.preprocessing as sp
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm
class DigitEncoder:
def fit_transform(self, x):
return x.astype(int)
def transform(self, x):
return x.astype(int)
def inverse_transform(self, x):
return x.astype(str)
data = []
data = np.loadtxt('traffic.txt', delimiter=',', dtype='U16')
data = data.T
encoders, x, y = [], [], []
for row in range(len(data)):
if data[row][0].isdigit():
encoder = DigitEncoder()
else:
encoder = sp.LabelEncoder()
if row < len(data) - 1:
x.append(encoder.fit_transform(data[row]))
else:
y = encoder.fit_transform(data[row])
encoders.append(encoder)
x = np.array(x).T
# 划分测试集训练集
train_x, test_x, train_y, test_y = ms.train_test_split(
x, y, test_size=0.25, random_state=7
)
# 使用SVM回归器训练模型
model = svm.SVR(kernel='rbf', C=10)
model.fit(train_x, train_y)
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))
# 执行业务预测
data = [['Tuesday', '13:35', 'San Francisco', 'yes']]
data = np.array(data).T
x = []
for row in range(len(data)):
encoder = encoders[row]
x.append(encoder.transform(data[row]))
x = np.array(x).T
pred_y = model.predict(x)
print(pred_y)
聚类
分类(classification)与聚类(cluster)不同, 分类是有监督学习模型, 聚类属于无监督学习模型. 聚类讲究使用一些算法把样本划分称为n个群落. 一般情况下, 这种算法都需要计算欧氏距离.
欧几里得距离
P
(
x
1
)
−
Q
(
x
2
)
:
∣
x
1
−
x
2
∣
=
(
x
1
−
x
2
)
2
P
(
x
1
,
y
1
)
−
Q
(
x
2
,
y
2
)
:
(
x
1
−
x
2
)
2
+
(
y
1
−
y
2
)
2
P
(
x
1
,
y
1
,
z
1
)
−
Q
(
x
2
,
y
2
,
z
2
)
:
(
x
1
−
x
2
)
2
+
(
y
1
−
y
2
)
2
+
(
z
1
−
z
2
)
2
P(x_1) - Q(x_2):|x_1 - x_2| = \sqrt{(x_1-x_2)^2} \\ P(x_1, y_1) - Q(x_2, y_2): \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2} \\ P(x_1, y_1, z_1) - Q(x_2, y_2, z_2): \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2 + (z_1 - z_2)^2} \\
P(x1)−Q(x2):∣x1−x2∣=(x1−x2)2P(x1,y1)−Q(x2,y2):(x1−x2)2+(y1−y2)2P(x1,y1,z1)−Q(x2,y2,z2):(x1−x2)2+(y1−y2)2+(z1−z2)2
用两个样本对应特征值之差的平方和的平方根, 即欧式距离, 用来表示两个样本的相似性.
K均值算法
第一步: 随机选择K个样本作为K个聚类中心, 计算每个样本到各个聚类中的欧氏距离, 将该样本分配到与之最近的聚类中心所在的类别中.
第二步: 根据第一步得到的聚类划分, 分别计算每个聚类的几何中心, 将几何中心作为新的聚类中心, 重复第一步, 直到计算所得几何中心与聚类中心重合或相近.
注意:
- 聚类数必须事先已知, 借助某些评估指标, 优选最好的聚类数量.
- 聚类中心的初始选择会影响到最终聚类划分的结果, 初始中心尽量选择距离较远的样本.
K举止相关API:
import sklearn.cluster as sc
# n_clusters: 聚类的类别数量.
model = sc.KMeans(n_clusters=4)
# 给出训练样本
model.fit(x)
# 预测样本类别
c = model.predict(x)
# 获取训练结果的聚类中心
centers = model.cluster_centers_
案例: 加载multiple3.txt, 完成KMeans聚类
import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp
x = np.loadtxt('multiple3.txt', delimiter=',')
# KMeans聚类模型
model = sc.KMeans(n_clusters=20)
model.fit(x)
pre_dict_c = model.predict(x)
centers = model.cluster_centers_
print(pre_dict_c)
# 计算分类边界
n = 500
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
grid_x, grid_y = np.meshgrid(
np.linspace(l, r, n),
np.linspace(b, t, n)
)
samples = np.column_stack(
(grid_x.ravel(), grid_y.ravel())
)
grid_z = model.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)
# 画图
mp.figure('K-Means Cluster', facecolor='lightgray')
mp.title('K-Means Cluster', fontsize=14)
mp.xlabel('x', fontsize=12)
mp.ylabel('y', fontsize=13)
mp.tick_params(labelsize=10)
# 绘制分类边界
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=pre_dict_c, cmap='brg', s=60, alpha=0.6, label='samples')
mp.scatter(centers[:, 0], centers[:, 1], color='orangered', marker='+', s=300, label='Centers')
mp.legend()
mp.show()
图像量化
KMeans聚类算法可以应用于图像量化领域. 通过KMeans算法把一张图像所包含的颜色值进行聚类划分, 求每一类别的平均值后, 再重新生成新的图像. 可以达到图像降维的目的, 更有利于图像识别. 这个过程称为图像量化.
案例:
import numpy as np
import scipy.misc as sm
import scipy.ndimage as sn
import sklearn.cluster as sc
import matplotlib.pyplot as mp
img = sm.imread('lily.jpg', True)
x = img.reshape(-1, 1) # n行1列
model = sc.KMeans(n_clusters=4)
model.fit(x)
y = model.labels_
centers = model.cluster_centers_.ravel()
# 把y数组中相应元素的值改为centers的值
# 生成新图片, 处理后, 图片中只包含centers范围内的值
img2 = centers[y].reshape(img.shape) # 利用掩码进行reshape
mp.figure('Image')
mp.subplot(121)
mp.axis('off')
mp.imshow(img, cmap='gray')
mp.subplot(122)
mp.axis('off')
mp.imshow(img2, cmap='gray')
mp.tight_layout()
mp.show()
均值漂移算法
首先假定样本空间的每个聚类均服从某个已知的概率分布规则, 然后用不同的概率密度函数拟合样本中的统计直方图, 不断移动密度函数的中心位置, 直到获得最佳的拟合效果为止. 这些概率密度函数曲线的峰值点就是聚类的中心, 再根据每个样本与各个中心的距离, 选择最近的聚类中心所属类别作为该样本的类别.
均值漂移算法的特点:
- 聚类数不必事先已知, 算法会自动识别出统计直方图的中心数量.
- 聚类中心不依据于最初假定, 聚类划分的结果相对稳定.
- 样本空间应该服从某种概率分布规则, 否则算法的准确性将会大打折扣.
均值漂移相关API:
# 获取量化带宽对象
bw = sc.estimate_bandwidth(
x, # 输入集
n_samples=len(x), #样本数量
quantile=0.1 # 量化带宽(统计直方图的宽度)
)
sc.Meanshift(bandwidth=bw, bin_seeding=True)
model.fit(x)
model.predict(x)
案例: multiple3.txt, 使用均值漂移做聚类划分.
import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp
x = np.loadtxt('multiple3.txt', delimiter=',')
# KMeans聚类模型
model = sc.KMeans(n_clusters=4)
model.fit(x)
pre_dict_c = model.predict(x)
centers = model.cluster_centers_
print(pre_dict_c)
...
凝聚层级算法
首先假定每个样本都是一个独立的聚类, 如果统计出来的聚类数大于期望的聚类数, 则从每个样本出发, 寻找离自己最近的另一个样本, 与之狙击, 形成更大的聚类. 同时令总聚类数减少, 不断重复以上过程, 直到统计出来的聚类数达到期望值为止.
凝聚层次算法的特点:
- 聚类数k必须事先已知. 借助某些评估指标, 优选最好的聚类数.
- 没有聚类中心概念, 因此只能在训练集中心划分聚类, 不能对训练集以外的未知样本确定其归属
- 在确定被凝聚的样本时, 出了以距离作为条件外, 还可以根据连续性来确定被凝聚的样本.
凝聚层次算法相关API:
# 构建凝聚层次聚类模型
model = sc.AgglometativeClustering(n_cluster=4)
# 训练 + 预测
pred_y = model.fit_predict(x)
案例:
import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp
x = np.loadtxt('multiple3.txt', delimiter=',')
# KMeans聚类模型
model = sc.KMeans(n_clusters=4)
model.fit(x)
pre_dict_c = model.predict(x)
centers = model.cluster_centers_
print(pre_dict_c)
# 计算分类边界
n = 500
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
grid_x, grid_y = np.meshgrid(
np.linspace(l, r, n),
np.linspace(b, t, n)
)
samples = np.column_stack(
(grid_x.ravel(), grid_y.ravel())
)
grid_z = model.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)
# 画图
mp.figure('K-Means Cluster', facecolor='lightgray')
mp.title('K-Means Cluster', fontsize=14)
mp.xlabel('x', fontsize=12)
mp.ylabel('y', fontsize=13)
mp.tick_params(labelsize=10)
# 绘制分类边界
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=pre_dict_c, cmap='brg', s=60, alpha=0.6, label='samples')
mp.scatter(centers[:, 0], centers[:, 1], color='orangered', marker='+', s=300, label='Centers')
mp.legend()
mp.show()
使用凝聚层次算法, 根据连续性来确定被聚集的样本类别划分相关API:
# linkage='average', 无连续性的凝聚层次聚类器
model = sc.AgglomerativeClustering(
linkage='average', n_clusters=4)
# 基于连续性的凝聚层次聚类器
# 近邻筛选器
conn = nb.kneighbors_graph(x, 10, include_self=False)
model = sc.AgglomerativeClustering(
linkage='average', n_clusters=4,
conntivity=conn)
案例:
import numpy as np
import sklearn.cluster as sc
import sklearn.neighbors as nb
import matplotlib.pyplot as mp
x = np.loadtxt('multiple3.txt', delimiter=',')
# 连续性的凝聚层次聚类器
# conn = nb.kneighbors_graph(x, 1, include_self=False)
# model = sc.AgglomerativeClustering(
# linkage='average', n_clusters=4,
# connectivity=conn)
model = sc.AgglomerativeClustering(
linkage='average', n_clusters=4
)
pre_dict_c = model.fit_predict(x)
print(pre_dict_c)
# 画图
mp.figure('Agglo Cluster', facecolor='lightgray')
mp.title('Agglo Cluster', fontsize=14)
mp.xlabel('x', fontsize=12)
mp.ylabel('y', fontsize=13)
mp.tick_params(labelsize=10)
mp.scatter(x[:, 0], x[:, 1], c=pre_dict_c, cmap='brg', s=60, alpha=0.6, label='samples')
mp.legend()
mp.show()
轮廓系数
轮廓系数用于评估聚类器的好坏. 好的聚类: 内密外疏, 同一个聚类内部的样本要足够密集, 不同的聚类之间样本要足够疏远.
轮廓系数的计算规则: 针对样本空间中的一个特定样本, 计算它与所在聚类其他样本的平均距离a, 以及该样本与距离最近的另一个聚类中所有样本的平均距离b, 该样本的轮廓系数为(b - a) / max(a, b). 将整个样本空间中所有样本的轮廓系数取算数平均值, 作为菌类划分的性能指标.
轮廓系数的取值区间: [-1, 1]. -1代表分类效果差, 1代表分类效果好. 0代表聚类重叠, 没有很好的划分.
轮廓系数相关API:
import sklearn.metrics as sm
# v: 返回样本空间所有样本的平均轮廓系数
# metric: euclidean 使用欧几里得距离算法
v = sm.silhouette_score(
输入集, 输出集,
sample_size=样本数,
metric=距离算法
)
案例:
# 输出轮廓系数指标
v = sm.silhouette_score(
x, pre_dict_c,
sample_size=len(x),
metric='euclidean'
)
print(v)
DBSCAN算法
从样本空间中任意选择一个样本, 以事先给定的半径做圆, 凡被该圆圈中的样本都视为与该样本处于相同的聚类, 以这些被圈中的样本为圆心继续画圆, 重复以上过程, 不断扩大被权重样本的规模, 直到再也没有新的样本加入为止, 至此得到一个聚类. 在剩余样本中, 重复以上过程, 直到耗尽样本空间中所有样本为止.
DBSCAN算法的特点:
- 事先给定的半径会影响最后的聚类效果, 可以借助轮廓系数选择较优方案.
- 根据聚类的形成过程, 把样本细分成为三类:
- 外周样本: 被其他样本聚集到某个聚类中, 但无法再引入新样本的样本
- 孤立样本: 聚类中的样本数低于所设定的下限, 则不称其为聚类, 称其为孤立样本.
- 核心样本: 除了外周样本和孤立样本以外的样本.
DBSCAN算法相关API:
# 构建DBSCAN聚类模型
# eps: 圆的半径
# min_samples: 聚类样本数量的下限, 低于该数值则为孤立yang'b
model = sc.DBSCAN(eps=0.5, min_samples=3)
pre_dict_c = model.fit_predict(x)
# 获取核心样本的下标
model.core_sample_indices_
案例: 读取perf.txt, 实现聚类划分.
import numpy as np
import sklearn.cluster as sc
import matplotlib.pyplot as mp
import sklearn.metrics as sm
x = np.loadtxt('perf.txt', delimiter=',')
# 基于轮廓系数优选eps超参数
eps, scores, models = np.linspace(0.3, 1.2, 10), [], []
for epsilon in eps:
model = sc.DBSCAN(eps=epsilon, min_samples=5)
model.fit(x)
# 输出轮廓系数指标
score = sm.silhouette_score(
x, model.labels_,
sample_size=len(x),
metric='euclidean'
)
scores.append(score)
models.append(model)
scores = np.array(scores)
best_index = scores.argmax()
print(eps[best_index])
print(scores[best_index])
# 获取到最优模型
model = models[best_index]
pre_dict_c = model.fit_predict(x)
# 获取核心样本掩码
core_mask = np.zeros(len(x), dtype=bool)
core_mask[model.core_sample_indices_] = True
# 获取孤立样本的掩码
offset_mask = model.labels_ == -1
# 外周样本的掩码
p_mask = ~(core_mask | offset_mask)
# 画图
mp.figure('Agglo Cluster', facecolor='lightgray')
mp.title('Agglo Cluster', fontsize=14)
mp.xlabel('x', fontsize=12)
mp.ylabel('y', fontsize=13)
mp.tick_params(labelsize=10)
labels = model.labels_
mp.scatter(x[core_mask][:, 0],
x[core_mask][:, 1],
c=labels[core_mask],
cmap='brg',
s=80, label='core')
mp.scatter(x[p_mask][:, 0],
x[p_mask][:, 1],
c=labels[p_mask],
cmap='brg',
alpha=0.5,
label='p', s=80)
mp.scatter(
x[offset_mask][:, 0],
x[offset_mask][:, 1],
c='gold', alpha=0.5,
label='offset', s=80
)
mp.legend()
mp.show()
推荐引擎
推荐引擎意在把最需要的推荐给用户.
在不同的机器学习场景中通常需要分析相似的样本, 而统计相似样本的方式可以基于欧氏距离分数, 也可以基于皮氏距离分数.
简单推荐引擎的过程:
- 当A登录后, 系统检索A看过(评过分)的电影, 发现A喜欢什么电影.
- 数据库中有哪些人与A的习惯相似. 找到相似用户.
- 检索相似用户看过的电影, 寻求评分较高的推荐给A.
欧氏距离分数
欧 式 距 离 分 数 = 1 1 + 欧 氏 距 离 欧式距离分数 = \frac{1}{1+欧氏距离} 欧式距离分数=1+欧氏距离1
计算所得欧氏距离分数区间处于(0, 1], 越趋向于0, 样本件的距离越远, 样本越不相似. 趋向于1, 代表越相似.
为了更方便的找到相似用户, 需要构建样本之间的欧式距离分数矩阵.
案例: 分析ratings.json
import json
import numpy as np
with open('ratings.json', 'r') as f:
ratings = json.loads(f.read())
# 所有用户
users = list(ratings.keys())
# scmat用于存储每个人之间的欧氏距离得分矩阵
scmat = []
for user1 in users:
scrow = []
for user2 in users:
# movies存储两个用户共同看过的电影
movies = set()
for movie in ratings[user1]:
if movie in ratings[user2]:
movies.add(movie)
if len(movies) == 0:
score = 0 # 两人没有共同语言, 得分为0
else:
# 计算两人相似度得分
# x, y保存两人同时看过的多部电影的评分
x, y = [], []
for movie in movies:
x.append(ratings[user1][movie])
y.append(ratings[user2][movie])
x = np.array(x)
y = np.array(y)
score = 1 / (1 + np.sqrt((x - y)**2).sum())
scrow.append(score)
scmat.append(scrow)
users = np.array(users)
scmat = np.array(scmat)
for scrow in scmat:
print(' '.join('{:.2f}'.format(score) for score in scrow))
皮尔逊相关系数
A = [2, 3, 4] # A用户打分列表
B = [3, 3, 3] # B用户打分列表
C = [3, 4, 5] # C用户打分列表
np.corrcoef(A, B)
np.corrcoef(A, C)
皮尔逊相关系数 = 协方差 / 标准差之积
相关系数处于[-1, 1]区间, 接近于-1, 代表两组样本负相关; 接近于1, 代表两组正相关.
案例:
import json
import numpy as np
with open('ratings.json', 'r') as f:
ratings = json.loads(f.read())
# 所有用户
users = list(ratings.keys())
# scmat用于存储每个人之间的欧氏距离得分矩阵
scmat = []
for user1 in users:
scrow = []
for user2 in users:
# movies存储两个用户共同看过的电影
movies = set()
for movie in ratings[user1]:
if movie in ratings[user2]:
movies.add(movie)
if len(movies) == 0:
score = 0 # 两人没有共同语言, 得分为0
else:
# 计算两人相似度得分
# x, y保存两人同时看过的多部电影的评分
x, y = [], []
for movie in movies:
x.append(ratings[user1][movie])
y.append(ratings[user2][movie])
x = np.array(x)
y = np.array(y)
score = np.corrcoef(x, y)[0, 1]
scrow.append(score)
scmat.append(scrow)
users = np.array(users)
scmat = np.array(scmat)
# 按照相似度从高到低排序每个用户的相似用户
for i, user in enumerate(users):
# 获取当前用户与所有人的相似度得分并排序
sorted_indices = scmat[i].argsort()[::-1]
# 不和自己比
sorted_indices = sorted_indices[sorted_indices != i]
# 与当前用户的所有相似用户及相似度得分
similar_users = users[sorted_indices]
similar_scores = scmat[i][sorted_indices]
# 生成推荐清单
# 1. 找到所有皮尔逊系数正相关的用户
# 2. 遍历每个相似用户, 找到相似看过的当前用户没看过的电影
# 3. 对于多个相似用户可能推荐同一部电影
# 取它们对该电影评分的均值作为推荐程度
positive_mask = similar_scores > 0
similar_users = similar_users[positive_mask]
similar_scores = similar_scores[positive_mask]
# 存储对于当前用户所推荐的电影, 以及电影推荐度(对电影的评分)
recomm_movies = {}
for i, similar_user in enumerate(similar_users):
for movie, score in ratings[similar_user].items():
if movie not in ratings[user].keys():
if movie not in recomm_movies:
recomm_movies[movie] = []
else:
recomm_movies[movie].append(score)
print(user)
recomm_movies = sorted(recomm_movies.items(), key=lambda x: np.average(np.array(x[1])), reverse=True)
print(recomm_movies)