最近两天心情很忐忑,文章更新晚了点,想了想,继续努力吧。
-
特征工程的定义
我的理解:在我看来,特征工程涵盖几个方面的内容,-
特征变量的选取
这一步对我们的模型有深远的影响,就像给人讲故事,那么故事从何说起呢,就从特征变量开始说起。要想让故事说的令人信服,那么对特征的选取就会有要求,特征选取的越有逻辑性,故事越具有说服力和可信度。
这里我深有体会,有一个观点可以在这里分享。我的观点是,“不要单一的去评价一个人或者事”。不是所有会机器学习算法的人就可以做好人工智能模型。特征工程就是第一个门槛,也叫做“经验”。说到这里肯定很多行业的人说,我有多年经验,你们做机器学习再牛逼的人还不是要向我请教为我服务。需要指正的是,这里的“经验”不是我们所说的行业经验,我很看不惯很多人说,我五年工作经验怎样怎样,倘若是单纯的上下班没有过多的思考,五年的行业经验在机器学习人工智能这里毫无意义。我所说的经验是对行业的思考,有的人思考半个小时就抓住了问题的关键,而有的人工作了五六年却依然不清楚问题的命脉在哪里,人和人是有差距的,所以不能局限的去看待一个人。所以,谁都别不看好谁,谁都别看低谁,需要具体人具体问题去分析。如果只会跑模型结果不好调参而逃离实际业务的人,这是一线工程师,如果只知道业务往来业务流程而不知道如何变现,这是一线业务员,如果了解算法了解模型了解业务coding能力强逻辑能力强,这是一线数据科学家。没有褒贬谁的意思,但实际上,人和人确实是不同的,脑力和脑量都会千差万别,所以,根据自身特点做自己擅长的事才是最聪明的做法。
回到主题,在实际的应用中,我们会发现我们有大量特征可以供我们选择,在当今这个信息化数据化的时代,只要你留心记录,有大量的数据可以供我们选择,我们担心的不是有没有数据用,而是如何去用这些数据。现实中,很多人把希望寄托在模型身上,这也解释了为什么这几年深度学习越来越火热。深度学习的本质就是神经网络,他可以对任何复杂的情况进行逻辑上的预测,所以,这种算法减轻了特征的思考,但是大大加大了深度学习的计算量。计算结果呢有的可以解释,有的无法解释。实际上,这不是我们唯一的选择,特征的选取如果正确,那么我们可以用很简单的机器学习模型就能实现人工智能预测。这种做法在特征工程中增加了人脑的思考,在模型上减少了模型的复杂度和计算量。trade-off就是这个道理,但是第二种做法会让我们的人工智能模型在解释上变得有逻辑也更方便让人理解。
-
数据的预处理
2.1 对缺失值的处理
缺失值的处理分为几种情况:
如果缺失值对应的数据列属于数值型数据,我们可以用前后数值的均值/中位数/众数来填充,具体问题具体分析。
如果缺失值对应的数据列属于文本型数据,我们则需要用统计的方法,这里可以直接统计文本数据,也可以先将文本数据转为虚拟变量然后统计,最终根据需要可以用中位数/众数来填充。
当然,如果数据样本足够大,缺失值所在的行并不多的情况下,我们可以直接对缺失值所在的整行进行删除。2.2 量纲的问题
我对量纲的理解是这样的,如果单位不同,那么不同特征离散值的范围就不同,如果某个特征的范围过大,会导致模型参数差异大,最终导致模型评估效果不佳。
因此,当我们把所有的特征变量都转化为无量纲,那么,每个特征的离散值都处于相同的值域内,这时候的模型效果会更好。
实现无量纲的方法有很多,包括:
0-1标准化(X-min)/(max-min),
Z标准化(X-mean)/std,
log归一化log_{10}(x)/log_{10}(max),
正则化Normalizer(d对每一个值除以范数,范数的求解如下表)
2.3 特征值类型的问题
特征值类型包含两大类,一类是文本型数据,一类是数值型数据。
对于文本型数据来说,我们需要进行数据转化,将其转化为离散型数值,也就是我们常说的虚拟变量,也叫做哑变量(dummy variable)。
如果是数值型数据,我们需要确定根据我们模型的特点而定。
如果我们尝试用逻辑回归或者SVM做二分类或者多分类的问题,这些数值我们可以直接用。
如果我们尝试用朴素贝叶斯来做二分类或者多分类,这时候,我们需要对离散的数值留意,是否可以代表一类,比如说,时间序列的数据,都是0.001的差别,但是其实我们的常识告诉我们,1.0002.000之间的所有值都是一种类别,那么我们需要将该特征的离散值泛化,用第三特征值表征这类数据。这里我们会用1来表示1.02.0之间的所有值,2表示2.0~3.0之间的所有值,以此类推。
-
-
以上就是我对特征工程的理解,接下来,说说具体python的实现手段。我会用两种方法实现,一种是自定义函数实现,一种调用python preprocessing包。
-
对缺失值的处理
举例使用的数据是
- 缺失值剔除
python实现
data.dropna() #高级语言,高级实现
函数实现
def findinx(data):
if len(data)==1:
if pd.isnull(data['豆瓣评分'][0]):
data.drop([0])
else:
index=[]
for i in range(len(data)):
if pd.isnull(data['豆瓣评分'][i]) or pd.isnull(data['累计票房'][i]) or pd.isnull(data['电影名称'][i]):
index.append(i)
data.drop(index)
return(data)
处理结果
- 缺失值填充(填充可以填充均值/中位数/众数)
python实现
from sklearn.preprocessing import Imputer
imputer=Imputer(strategy='mean') #mean, median, most_frequent
data['累计票房']=imputer.fit_transform(data[['累计票房']])
data['豆瓣评分']=imputer.fit_transform(data[['豆瓣评分']])
函数实现
def findinx(data):
if len(data)==1:
if data['豆瓣评分'][0]==np.nan:
data.drop([0])
else:
index=[]
for i in range(len(data)):
if pd.isnull(data['豆瓣评分'][i]):
data['豆瓣评分'][i]=np.mean(data['豆瓣评分'])
if pd.isnull(data['累计票房'][i]):
data['累计票房'][i]==np.mean(data['累计票房'])
return(data)
处理结果
-
量纲的转化
举例使用的数据
在上面的数据中我们发现,累计票房和豆瓣评分的数据跨度相差很大,如果我们不做量纲的转化,会导致我们模型很容易“扁长化”。
0-1标准化
python实现
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
data['标准累计票房'] = scaler.fit_transform(data[['累计票房']])
data['标准豆瓣评分']=scaler.fit_transform(data[['豆瓣评分']])
函数实现
def minmaxscaler1(data,columns):
#columns=['累计票房','豆瓣评分']
for column in columns:
data['标准化'+column]=(data[column]-np.min(data[column]))/(np.max(data[column])-np.min(data[column]))
return(data)
Z标准化
python实现
from sklearn.preprocessing import scale
data3['标准累计票房'] = scaler.fit_transform(data3[['累计票房']])
data3['标准豆瓣评分'] = scaler.fit_transform(data3[['豆瓣评分']])
函数实现
def zscale(data,columns):
#columns=['累计票房','豆瓣评分']
for column in columns:
data['标准化'+ column]=(data[column]-np.mean(data[column]))/np.std(data[column])
return(data)
Normalizer归一化
Normalizer一半用于TF-IDF的矩阵中
from sklearn.preprocessing import Normalizer
scaler=Normalizer(norm='l1') # l1, l2
data['标准累计票房']=scaler.fit_transform(data[['累计票房']].transpose()).transpose()
data['标准豆瓣评分']=scaler.fit_transform(data[['豆瓣评分']].transpose()).transpose()
函数实现
def pl1(data):
sumvalue=0
for i in data:
sumvalue=sumvalue+np.abs(i)
data=data/sumvalue
return(data)
def pl2(data):
sumvalue=0
for i in data:
sumvalue=sumvalue+i**2
data=data/(np.sqrt(sumvalue))
return(data)
def normalizer(data, columns, way):
#way='l1' or 'l2'
for column in columns:
if way=='l1':
data['归一化'+column]=pl1(data[column])
elif way=='l2':
data['归一化'+column]=pl2(data[column])
return(data)
可以看出我们函数计算的结果和调包计算的结果没有差别。
-
文本变量转虚拟变量
转虚拟变量一半用dict就可以实现,当然python有现成包。
我们举例的数据如下。
python实现
dummiesData=pd.get_dummies(data4, columns=['症状'], prefix=['症状'], prefix_sep="_")
dummiesData = pd.get_dummies(
data4,columns=['职业','疾病','症状'],prefix=['职业','疾病','症状'],prefix_sep="_"
)
python这种虚拟变量的做法容易增加数据特征的维度。
函数实现
def dummyvariable(names):
dummies=np.linspace(1,len(names),len(names))
return(dummies)
def dummy(data):
newdata=pd.DataFrame()
columns=data.columns
feature={}
for column in columns:
feature[column] = data[column].unique()
dummy={}
dummies=dummyvariable(feature[column])
for i in range(len(feature[column])):
dummy[feature[column][i]]=dummies[i]
newdata[column]=data[column].map(dummy)
return(newdata)
可以看出,我们已经将文本的数据转化成了数值型数据。
接下来,会写到,特征的分析以及降维的一些问题。