4. 特征工程(下):基于IC曲线的特征提取
好了,咱们接着聊特征工程的下半场。
上一章我们讲了基础特征怎么抓,这一章要动真格的了——IC曲线。说白了,就是容量增量曲线。这东西在电池寿命预测里,地位相当于心电图在心脏科的地位。我个人习惯,拿到一批电池数据,第一件事就是画IC曲线看看。
4.1 为什么IC曲线这么香?
你想想看,电池充放电的电压平台变化,其实藏着很多信息。但直接看电压-容量曲线,很多细节被淹没了。IC曲线相当于一个「放大镜」,把电压平台上的微小变化给凸显出来。
我在项目中遇到过一件事:两批电池,外观一样,容量曲线几乎重合。但IC曲线一画,峰值位置差了0.02V。后来拆解发现,是正极材料批次差异。嗯,从那以后我再也不敢只看容量曲线了。
核心思想:IC曲线 = dQ/dV,即容量对电压的微分。峰值对应相变过程,峰高对应反应速率,峰面积对应容量贡献。
4.2 峰值特征提取:位置、高度、面积
提取IC曲线特征,我一般分三步走:
- 平滑处理——原始dQ/dV噪声大,先滤波
- 峰值检测——找局部极大值
- 特征量化——记录位置、高度、半峰宽、面积
代码实现其实不复杂,我贴一段常用的:
import numpy as np
from scipy.signal import find_peaks, savgol_filter
def extract_ic_features(voltage, capacity):
"""
从IC曲线提取峰值特征
voltage: 电压序列 (V)
capacity: 容量序列 (Ah)
"""
# 1. 计算dQ/dV
dq = np.diff(capacity)
dv = np.diff(voltage)
ic = dq / dv
# 2. 平滑处理 - Savitzky-Golay滤波器
ic_smooth = savgol_filter(ic, window_length=11, polyorder=3)
# 3. 峰值检测
peaks, properties = find_peaks(
ic_smooth,
prominence=0.1, # 峰显著度
width=5, # 最小峰宽
distance=10 # 最小峰间距
)
# 4. 提取特征
features = {}
for i, peak_idx in enumerate(peaks):
features[f'peak_{i}_position'] = voltage[peak_idx]
features[f'peak_{i}_height'] = ic_smooth[peak_idx]
# 半峰宽
left = properties['left_ips'][i]
right = properties['right_ips'][i]
features[f'peak_{i}_width'] = voltage[right] - voltage[left]
# 峰面积(近似梯形积分)
area = np.trapz(
ic_smooth[left:right],
voltage[left:right]
)
features[f'peak_{i}_area'] = area
return features
避坑指南:我曾经吃过一个亏——直接用原始数据算dQ/dV,结果噪声把峰值全淹了。后来养成了习惯:先平滑,再求导。顺序不能反。
4.3 特征相关性分析与筛选
特征多了不是好事。你想想看,如果两个特征高度相关,它们其实在说同一件事。我见过有人一口气提取了50多个IC特征,结果模型过拟合得一塌糊涂。
我的做法是:
- 计算相关系数矩阵——皮尔逊或斯皮尔曼
- 设定阈值——相关系数 > 0.9 的,只保留一个
- 结合业务理解——有些特征虽然相关,但物理意义不同,要斟酌
举个例子:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# 假设features_df是特征数据框
corr_matrix = features_df.corr(method='spearman')
# 找出高相关对
high_corr_pairs = []
for i in range(len(corr_matrix.columns)):
for j in range(i+1, len(corr_matrix.columns)):
if abs(corr_matrix.iloc[i, j]) > 0.9:
high_corr_pairs.append(
(corr_matrix.columns[i],
corr_matrix.columns[j],
corr_matrix.iloc[i, j])
)
print(f"发现 {len(high_corr_pairs)} 对高相关特征")
注意:相关性高不代表冗余。比如峰值位置和峰面积,虽然相关,但一个反映热力学,一个反映动力学。我一般会保留,除非模型表现明显变差。
4.4 特征归一化与标准化
这一步很多人会忽略,但实际影响很大。为什么?
你想想看,峰值位置是3.5V左右,峰面积可能是0.5,高度可能是2.0。量纲不同,直接扔进模型,大数值的特征会主导学习过程。
我常用的两种方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Min-Max归一化 | (x - min) / (max - min) | 特征有明确边界,如电压范围 |
| Z-score标准化 | (x - mean) / std | 特征分布近似正态,无边界约束 |
我个人习惯:IC峰值位置用Min-Max,因为电压范围固定(比如3.0V-4.2V)。峰高和峰面积用Z-score,因为它们受电池容量衰减影响,分布会漂移。
from sklearn.preprocessing import MinMaxScaler, StandardScaler
# 峰值位置 - 归一化
pos_scaler = MinMaxScaler(feature_range=(0, 1))
peak_pos_norm = pos_scaler.fit_transform(peak_pos_features)
# 峰高/面积 - 标准化
height_scaler = StandardScaler()
peak_height_std = height_scaler.fit_transform(peak_height_features)
小技巧:标准化时,记得用训练集的均值和标准差去变换测试集。我见过有人把整个数据集一起标准化,结果造成数据泄露,模型评估虚高。
4.5 多维特征融合策略
单一IC特征不够用,怎么办?融合。
我常用的融合策略有三种:
- 串联融合——直接把IC特征和电压特征、温度特征拼起来。简单粗暴,但特征维度会膨胀。
- 加权融合——根据特征重要性给权重。比如用随机森林的特征重要性排序,前10个特征权重高一些。
- 降维融合——用PCA或自编码器把高维特征压缩。适合特征数量超过样本数量的情况。
举个例子,串联融合的代码:
import numpy as np
# IC特征 (N_samples, N_ic_features)
ic_features = extract_all_ic_features(data)
# 基础特征 (N_samples, N_base_features)
base_features = extract_base_features(data)
# 串联融合
fused_features = np.hstack([ic_features, base_features])
print(f"融合后特征维度: {fused_features.shape[1]}")
不过我要提醒一句:不是特征越多越好。我做过一个实验,IC特征从10个增加到20个,模型精度只提升了0.5%,但训练时间翻了一倍。性价比不高。
4.6 本章知识体系
为了让你更直观地理解,我画了一张流程图:
这张图把整个流程串起来了。从原始数据到最终模型输入,每一步都有讲究。我个人觉得,特征工程占整个项目60%的工作量,模型反而是相对简单的部分。
总结一下:IC曲线特征提取的核心是峰值的位置、高度和面积。特征筛选要兼顾统计相关性和物理意义。归一化标准化不能省,融合策略要根据数据量灵活选择。
好了,这一章就到这里。下一章我们聊聊特征工程里另一个重要话题——时间序列特征的构造方法。