总体思路
1. 数据处理
空值处理(nan值)填补方法:拉格朗日插值,均值填补,众数填补,回归填补,k-means填补,或直接删掉空值
特征转换,例如one-hot,文本转数值(bag of words)
归一化:把数据缩到0到1之间,正态化例如softmax
样本点少的可以删除
2. 预分析
2.1 画图
热力图
小提琴图
散点矩阵图
多元散点图
Hexbin图
界定相关数据,并且进行直观的观察和分析(画图观察数据(画图多样性:词云图,box plot))),寻找建模所用的特征,判断有无相关关系
2.2 特征分类
特征分类:
(1)长期特征:提前3个月得到的确定值
(2)短期特征:不能提前3个月获取值的特征
聚类(分类)模型:
k-means代表连续型数据集
k-mode用于离散型数据集
dbscan用来做分类的,用于连续型,比如一个班的同学都在一起就可以看作一类
3. 建模
经过多次实验对多个模型进行检验,选择合适的模型,并进一步对特征进行选择(有些特征可能用处不大,可PCA降维)
3.1. 过拟合处理方法
3.1.1. 特征选择
Filter方法
先对数据集进行特征选择,然后再训练模型。该方法独立于模型本身,因为比wrapper方法更general,计算性能更好,所以有时候在Wrapper前先用它。
-
皮尔逊相关系数 Pearson Correlation(就是我们统计学的那个相关系数)
一般只用于线性关系的判断。from scipy.stats import pearsonr import numpy as np pccs = np.corrcoef(x, y) # numpy的函数 或 pccs = pearsonr(x, y) # scipy.stats的函数
-
互信息和最大信息系数 Mutual information and maximal information coefficient (MIC)
- MIC可以识别各种函数关系,例如:正弦函数和双曲线函数和直线。
- 如果没有噪音的直线关系和没有噪音的正弦函数关系,他们的MIC都是1,加上相同的噪音之后,如果线性关系的MIC变成0.7了,那么正弦函数关系的MIC也变成0.7,换句话说,噪音对MIC造成的影响与变量之间的函数关系无关,但这一论证在一篇论文中被反驳了,或者说部分反驳了。
import numpy as np from minepy import MINE def print_stats(mine): print("MIC", mine.mic()) # 返回MIC x = np.linspace(0, 1, 1000) y = np.sin(10 * np.pi * x) + x mine = MINE(alpha=0.6, c=15) mine.compute_score(x, y) print("Without noise:") print_stats(mine) print() np.random.seed(0) y += np.random.uniform(-1, 1, x.shape[0]) # add some noise mine.compute_score(x, y) print("With noise:") print_stats(mine)
-
距离相关系数 (Distance correlation)
为了克服皮尔逊相关系数的弱点,即使皮尔逊相关系数=0,这两个变量可能是非线性相关,但如果距离相关稀疏为0,那么这两个变量就是独立的了。dcorr(u,v)越大,u和v的相关性越强
from scipy.spatial.distance import pdist, squareform import numpy as np def distcorr(X, Y): """ Compute the distance correlation function # >>> a = [1,2,3,4,5] # >>> b = np.array([1,2,9,4,4]) # >>> distcorr(a, b) 0.762676242417 """ X = np.atleast_1d(X) Y = np.atleast_1d(Y) if np.prod(X.shape) == len(X): X = X[:, None] if np.prod(Y.shape) == len(Y): Y = Y[:, None] X = np.atleast_2d(X) Y = np.atleast_2d(Y) n = X.shape[0] if Y.shape[0] != X.shape[0]: raise ValueError('Number of samples must match') a = squareform(pdist(X)) b = squareform(pdist(Y)) A = a - a.mean(axis=0)[None, :] - a.mean(axis=1)[:, None] + a.mean() B = b - b.mean(axis=0)[None, :] - b.mean(axis=1)[:, None] + b.mean() dcov2_xy = (A * B).sum() / float(n * n) dcov2_xx = (A * A).sum() / float(n * n) dcov2_yy = (B * B).sum() / float(n * n) dcor = np.sqrt(dcov2_xy) / np.sqrt(np.sqrt(dcov2_xx) * np.sqrt(dcov2_yy)) return dcor a = [1,2,3,4,5] b = np.array([1,2,9,4,4]) dcor = distcorr(a, b) print(dcor)
-
基于学习模型的特征排序 (Model based ranking)
Wrapper方法
直接把最终将要使用的模型的性能作为特征子集的评价标准
- SBS:从特征全集里逐个去除单个特征,直至剩余的特征子集达到期望的维度。那么如何逐个去除呢?我们先定义一个标准函数J(选一个metrics score如accuracy score),借助J的变化来判断每一步应该去除哪个特征。
- 如果metric score越大越好,那么判断标准就是:当去除一个特征带来accuracy score增加得最多时(accuracy score降低时取降低得最少),去除该特征。
- 如果metric score越小越好(RMSE),那么判断标准就是:当去除一个特征带来accuracy score降低得最多(增加得最少)时,我们就去除这个特征。
- 总结:每一步选择去掉的是最不会导致模型变更差的那个特征。
- sklearn中的RFE (Recursive feature elimination)的主要思路是反复构建模型(如SVM或者回归模型)然后选出最好的(或者最差的)的特征(可以根据系数来选),把选出来的特征放到一边,接着在剩余的特征上重复以上过程,直到遍历了所有特征。类似SBS的思路。
from sklearn.feature_selection import RFE from sklearn.linear_model import LinearRegression from sklearn.datasets import load_boston boston = load_boston() X = boston["data"] Y = boston["target"] names = boston["feature_names"] #use linear regression as the model lr = LinearRegression() #rank all features, i.e continue the elimination until the last one rfe = RFE(lr, n_features_to_select=1) rfe.fit(X,Y) print("Features sorted by their rank:") print(sorted(zip(rfe.ranking_, names)))
- 稳定性选择:sklearn中的RSM (Randomized sparse models)其主要思想是在不同的数据子集和特征子集上运行特征选择算法,不断的重复,最终汇总特征选择结果,比如可以统计某个特征被认为是重要特征的频率(被选为重要特征的次数除以它所在的子集被测试的次数)。越重要的特征的得分会越接近1。
from sklearn.linear_model import RandomizedLasso from sklearn.datasets import load_boston boston = load_boston() #using the Boston housing data. #Data gets scaled automatically by sklearn's implementation X = boston["data"] Y = boston["target"] names = boston["feature_names"] rlasso = RandomizedLasso(alpha=0.025) rlasso.fit(X, Y) print("Features sorted by their score:") print(sorted(zip(map(lambda x: format(x, '.4f'), rlasso.scores_), names), reverse=True))
Embedded方法
- 线性模型和正则化:
- 线性模型
- 越重要的特征对应的系数就会越大,而跟输出变量越是无关的特征对应的系数就会越接近于0,起相反作用的特征对应的系数是小于0。
from sklearn.linear_model import LinearRegression import numpy as np # A helper method for pretty-printing linear models def pretty_print_linear(coefs, names=None, sort=False): if names == None: names = ["X%s" % x for x in range(len(coefs))] lst = zip(coefs, names) if sort: lst = sorted(lst, key=lambda x: -np.abs(x[0])) return " + ".join("%s * %s" % (round(coef, 3), name) for coef, name in lst) np.random.seed(0) # 有了这段代码,下次再生成随机数的时候,与上次一样的结果 size = 5000 # 表示抽取多少个样本 # 随机生成3个特征的样本,每个维度的特征都是服从期望为0,标准差为1的正态分布 X = np.random.normal(0, 1, (size, 3)) # 抽取5000个样本,每个样本都是3维的 # Y = X0 + 2*X1 + noise Y = X[:, 0] + 2 * X[:, 1] + np.random.normal(0, 2, size) lr = LinearRegression() lr.fit(X, Y) print("Linear model:", pretty_print_linear(lr.coef_))
- 多重共线性问题:在很多实际的数据当中,往往存在多个互相关联(X1≈X2)的特征,这时候模型就会变得不稳定,对噪声很敏感,数据中细微的变化就可能导致模型的巨大变化
答案:>>> Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2size = 100 # 另外一个种子为5的随机采样,若要执行相同的结果,以种子号来区分随机采样的结果 np.random.seed(seed=5) X_seed = np.random.normal(0, 1, size) X1 = X_seed + np.random.normal(0, .1, size) X2 = X_seed + np.random.normal(0, .1, size) X3 = X_seed + np.random.normal(0, .1, size) X = np.array([X1, X2, X3]).T Y = X1 + X2 + X3 + np.random.normal(0, 1, size) lr = LinearRegression() lr.fit(X, Y) print("Linear model:", pretty_print_linear(lr.coef_))
- 越重要的特征对应的系数就会越大,而跟输出变量越是无关的特征对应的系数就会越接近于0,起相反作用的特征对应的系数是小于0。
- **L1正则化(Lasso Regression)的特点是迫使那些弱的特征所对应的系数为0,**因此L1正则化往往会使学到的模型很稀疏。这既是优点也是缺点,看情况而定。
from sklearn.linear_model import Lasso from sklearn.preprocessing import StandardScaler from sklearn.datasets import load_boston import numpy as np boston = load_boston() scaler = StandardScaler() X = scaler.fit_transform(boston["data"]) Y = boston["target"] names = boston["feature_names"] lasso = Lasso(alpha=.3) lasso.fit(X, Y) print("Lasso model: ", pretty_print_linear(lasso.coef_, names, sort=True))
- L2正则化(Ridge Regression)让系数的取值变得平均,较为稳定,系数不太会因为细微的数据变化而波动,不像L1正则化那样。可以处理多重共线性
import numpy as np from sklearn.linear_model import LinearRegression from sklearn.linear_model import Ridge size = 100 # We run the method 10 times with different random seeds for i in range(10): print("Random seed %s" % i) np.random.seed(seed=i) X_seed = np.random.normal(0, 1, size) X1 = X_seed + np.random.normal(0, .1, size) X2 = X_seed + np.random.normal(0, .1, size) X3 = X_seed + np.random.normal(0, .1, size) Y = X1 + X2 + X3 + np.random.normal(0, 1, size) X = np.array([X1, X2, X3]).T lr = LinearRegression() lr.fit(X, Y) # 普通LR print("Linear model:", pretty_print_linear(lr.coef_)) ridge = Ridge(alpha=10) ridge.fit(X, Y) # L2 print("Ridge model:", pretty_print_linear(ridge.coef_))
- 树算法
- 随机森林
- 平均不纯度减少(mean decrease impurity)
随机森林由多个决策树构成。利用不纯度可以确定节点(最优条件),对于分类问题,通常采用基尼不纯度或者信息增益,对于回归问题,通常采用的是方差或者最小二乘法拟合。当训练决策树的时候,可以计算出每个特征减少了多少树的不纯度。对于一个决策树森林来说,可以算出每个特征平均减少了多少不纯度,并把它平均减少的不纯度作为特征选择的值。from sklearn.datasets import load_boston from sklearn.ensemble import RandomForestRegressor import numpy as np #Load boston housing dataset as an example boston = load_boston() X = boston["data"] Y = boston["target"] names = boston["feature_names"] rf = RandomForestRegressor() rf.fit(X, Y) print("Features sorted by their score:") print(sorted(zip(map(lambda x: "%.4f"%x, rf.feature_importances_), names), reverse=True))
- 平均精确率减少 (mean decrease accuracy)
1. 这种方法直接度量每个特征对模型精确率的影响。
2. 主要思路:打乱每个特征的特征值顺序,并且度量顺序变动对模型的精确率的影响。对于不重要的变量来说,打乱顺序对模型的精确率影响不会太大;对于重要的变量来说,打乱顺序就会降低模型的精确率。from sklearn.model_selection import ShuffleSplit from sklearn.metrics import r2_score from sklearn.datasets import load_boston from collections import defaultdict from sklearn.ensemble import RandomForestRegressor import numpy as np boston = load_boston() X = boston["data"] Y = boston["target"] names = boston["feature_names"] rf = RandomForestRegressor() scores = defaultdict(list) # crossvalidate the scores on a number of different random splits of the data rs = ShuffleSplit(len(X), 100, .3) for train_idx, test_idx in rs.split(X): X_train, X_test = X[train_idx], X[test_idx] Y_train, Y_test = Y[train_idx], Y[test_idx] r = rf.fit(X_train, Y_train) acc = r2_score(Y_test, rf.predict(X_test)) for i in range(X.shape[1]): X_t = X_test.copy() np.random.shuffle(X_t[:, i]) shuff_acc = r2_score(Y_test, rf.predict(X_t)) scores[names[i]].append((acc - shuff_acc) / acc) print("Features sorted by their score:") print(sorted( [(float('%.4f'%np.mean(score)), feat) for feat, score in scores.items()], reverse=True) )
- 平均不纯度减少(mean decrease impurity)
- 决策树
- ID3(只用于连续型),C4.5,CART树
- 随机森林
- 线性模型
3.1.2. 降维
- PCA (KPCA)
- SVD
对无label数据的处理思路:
通过无监督的方式先分类,再手动给标签,然后进行有监督学习
直接手动标记(annotation),进行有监督学习
4. 评价
层次分析法(AHP)
AIC
BIC
5. 优缺点
参考文章:
机器学习的特征选择方法总结
sklearn里的特征选择方法
特征选择方法—Filter,Wrapper,Embedded
基于模型的特征选择详解 (Embedded & Wrapper)
请叫我Mr. Unbelievable——预测北京市每日车辆事故率
特征选择, 经典三刀
Minepy—使用python计算最大互信息系数(MIC)
特征工程之距离相关系数( Distance correlation coefficient )
Distance correlation(距离相关系数)
常用距离算法和相关系数及其Python实现