首先介绍一下比赛背景。这个比赛是中电投的一个关于风机开裂故障分析的预警的比赛。
训练数据有将近5万个样本,测试数据有将近9万个样本。数据来自于SCADA采集系统。采集了10分钟之内的75个特征值的数据信息,label是一周以内风机是否会发生故障的label。
02 数据介绍
每个样本10分钟之内大概采集到了450条数据,一共75个特征,也就是差不多75*450个信息。最后三个特征完全没有数据,所以一开始做的时候,我们就把最后三个特征进行删除,实际上我们是对72个特征进行的数据分析。最开始,用的是seaborn画的正常风机和不正常风机的频率分布图,比如说对于轮毂转速这个特征:
1
import
seaborn
as
snsimport pandas
as
pd
2
data_file =
r"D:\fan_fault\feature1.csv"
3
pre_process = pd.read_csv(data_file, encoding =
"gbk"
)
4
5
pre_process = pre_process.fillna(
0
)
6
feature1_plot = pre_process[
"normal(0)"
]
7
8
feature2_plot2 = pre_process[
"fault(1)"
]
9
sns.kdeplot(feature1_plot, shade =
True
)
10
sns.kdeplot(feature2_plot2, shade =
True
)
大部分特征都是这样,没有很好地区分度。正是因为这样,也一直没有尝试出来非常好的模型。后来我们尝试用MATLAB画图,每个特征出两个图
看起来要比seaborn好一些(后两个图和第一个不是一个特征)。我们在做数据分析这一块一个很大的问题是在于只去查了各个特征的物理含义,做了频率和频数分布图,看看是否有没有好的特征,然后就直接进入了下一步。忘了考虑是否可能会出现因为采集的问题而导致的异常值和空缺值的问题。这一点导致后面我们很多工作推到从来。
03 数据分析
我们从统计上来看,并没有找到很好区分度的特征,然后就考虑从物理上来找。在老师的建议下,我们尝试了有轮毂转速,风速为6.5m/s时,y方向振动值的特征:
依旧没有很好的区分度,对于其他风速尝试,也是如此。
我们讨论阈值、记0等方式构造新特征。就在说道记0这个新特征构造办法的时候,突然发现,大气压力这个特征,居然有0的情况。根据物理学的知识来讲,风机的大气压力是不可能为0的。然后我们才想起来,没有对数据的异常值进行处理。删除了有8万多条整行全为0的数据,导致某些文件为空,也就是这个风机没有数据信息。当然,也有某些风机是某几行为0。除了删除空缺值,我们还对其他明显是异常的数据进行了一些数据清洗工作。因为之前我们对于数据特征数统计分析是根据未清洗的数据做的分析,所以分析的可靠性也有点问题,后面我们在做工作的时候,有了一些不必要的麻烦。我们也做了一些相关性分析的工作,大部分特征相关性十分的高。几十个特征两两组合然后进行相关性分析,会有数千个结果,相关性分析没有办法进行下去。后来,我们就没有考虑相关性的事情。
04 特征工程
我们最开始尝试对前72个特征构造均值,作为基准尝试看看效果如何。
1
import
os
2
import
pandas
as
pd
3
import
numpy
as
np
4
import
csv
5
6
label_file =
r"C:\fan_fault\train\trainX"
7
train_mean =
r"D:\fan_fault\train_mean_new.csv"
8
9
with
open(train_mean,
"a"
, newline =
''
, encoding =
"utf-8"
)
as
f:
10
train_mean = csv.writer(f)
11
12
for
x
in
range(
1
,
48340
):
13
fan_file = os.path.join(label_file, str(x) +
".csv"
)
14
print(
"程序运行进度为"
, x/
48340
)
#用该语句查看工作进度状态
15
16
with
open(fan_file, encoding=
'utf-8'
)
as
f:
17
feature_read = pd.read_csv(f)
18
#遍历打开文件的每一个特征(72),求取均值
19
# a用来临时存放计算好的特征均值,外加一个label
20
21
a = []
22
for
i
in
range(
72
):
23
mean_num = feature_read.iloc[:, i]
24
mean_num = np.array(mean_num).mean()
25
#生成每个特征所有数据对应的均值
26
a.append(mean_num)
27
28
train_mean.writerow(a)
也包括绝对值差分累计、差分均值、差分方差,用随机森林进行调参。
1
# -*- coding: utf-8 -*-"""
2
3
import
numpy
as
np
4
import
pandas
as
pd
5
from
sklearn.preprocessing
import
MinMaxScaler
6
from
sklearn.ensemble
import
RandomForestClassifier
7
from
sklearn.model_selection
import
cross_val_scorefrom sklearn
8
import
metrics
9
from
sklearn.model_selection
import
GridSearchCV
10
11
#数据导入、检查空缺值
12
data = pd.read_csv(
r'D:\next\8_19\train_data.csv'
,encoding =
"gbk"
)
13
label = pd.read_csv(
r"D:\next\8_19\train_label.csv"
)
14
data.info()
15
data.notnull().sum(axis=
0
)/data.shape[
0
]
16
train = data.iloc[:,:
-1
]
17
label = label.iloc[:,
-1
]
18
19
#数据标准化
20
scaler = MinMaxScaler()
21
train = scaler.fit(train).transform(train)
22
23
#单个分类器
24
clf = RandomForestClassifier(random_state=
14
)
25
f1 = cross_val_score(clf, train, label, scoring=
'f1'
)
26
print(
"f1:{0:.1f}%"
.format(np.mean(f1)*
100
))
27
28
#调参
29
parameter_space = {
30
'n_estimators'
:range(
10
,
200
,
10
),
31
'max_depth'
:range(
1
,
10
),
32
'min_samples_split'
:range(
2
,
10
),
33
}
34
clf = RandomForestClassifier(random_state=
14
)
35
grid = GridSearchCV(clf,parameter_space,scoring=
'f1'
, n_jobs =
6
)
36
grid.fit(train,label)
37
print(
"f1:(0:.1f)%"
.format(grid.best_score_*
100
))
38
print(grid.best_estimator_)
39
40
#调参后的分类器
41
new_clf = RandomForestClassifier(bootstrap=
True
, class_weight=
None
, criterion=
'gini'
,
42
max_depth=
7
, max_features=
'auto'
, max_leaf_nodes=
None