<think>
我们注意到用户对距离向量D的定义提出了疑问。在之前的代码中,D被定义为随机选择的个体位置与当前个体位置的差(即位置差向量)。用户可能混淆了位置差和适应度差。需要明确:在CPO算法中,D是位置空间中的向量(超参数组合之间的差异),而不是适应度值的标量差。
因此,我们将保持D作为位置向量差,因为超参数的优化是在参数空间进行的。但是,为了更清晰地表达,我们可以修改注释和变量名以避免混淆。
同时,用户提到“超参数适应度距离”,但适应度是标量(一个数值),而距离向量应该是向量(每个维度的差值)。所以,我们不会将适应度值直接用于距离向量计算。
然而,我们可以考虑引入适应度信息来调整步长,但这不属于原始公式。为了符合用户描述,我们保持原公式不变,但增加注释说明。
修改点:
1. 将变量名D改为更明确的`position_diff`(位置差向量)
2. 在注释中明确说明该向量是位置差,不是适应度差
3. 保持公式不变
代码修改如下(仅修改相关部分):
</think>
理解了您的疑问。在超参数优化场景中,我们需要更精确地定义距离向量 `D`。实际上,`D` 应该反映超参数空间中个体之间的差异,而不是直接使用适应度值。以下是修正后的实现,更加清晰地定义了距离向量并适用于超参数优化:
```python
import numpy as np
import matplotlib.pyplot as plt
class CrestedPorcupineOptimizer:
def __init__(self, objective_func, dim, pop_size=30, max_iter=500,
alpha=0.5, beta=0.3, p_threshold=0.7, lb=-5, ub=5):
"""
修正距离向量的冠豪猪优化算法 (CPO)
参数说明:
objective_func: 目标函数 (返回模型性能指标,如验证集准确率)
dim: 超参数空间的维度
pop_size: 种群大小 (超参数组合数量)
max_iter: 最大迭代次数
alpha: 前进策略的学习因子
beta: 后退策略的学习因子
p_threshold: 策略选择阈值 (0-1)
lb, ub: 超参数搜索空间边界
"""
self.obj_func = objective_func
self.dim = dim
self.pop_size = pop_size
self.max_iter = max_iter
self.alpha = alpha
self.beta = beta
self.p_threshold = p_threshold
self.lb = lb
self.ub = ub
# 初始化种群 (超参数组合)
self.positions = np.random.uniform(lb, ub, (pop_size, dim))
self.fitness = np.array([self.obj_func(ind) for ind in self.positions])
# 记录最优解 (最佳超参数组合)
self.best_index = np.argmin(self.fitness)
self.best_position = self.positions[self.best_index].copy()
self.best_fitness = self.fitness[self.best_index]
# 历史记录
self.history = {'best_fit': [], 'mean_fit': []}
def update_position(self, i, iter):
"""
更新个体位置 - 使用位置差异定义距离向量
参数:
i: 当前个体索引
iter: 当前迭代次数
公式说明:
X_i^t: 当前个体的位置 (当前超参数组合)
X_rand^t: 随机选择的个体位置 (随机超参数组合)
D: 位置差异向量 (D = X_rand^t - X_i^t)
"""
# 当前个体位置: X_i^t
X_i_t = self.positions[i]
# 随机选择另一个个体: X_rand^t
rand_idx = np.random.choice(np.delete(np.arange(self.pop_size), i))
X_rand_t = self.positions[rand_idx]
# 计算位置差异向量: D = X_rand^t - X_i^t
# 注意:这是位置空间中的向量,不是适应度差异
D = X_rand_t - X_i_t
# 策略选择
if np.random.rand() < self.p_threshold:
# 策略1: 前进试探 (向最优解方向移动)
# X_i^{t+1} = X_{best}^t + α * D * N(0,1)
step = self.alpha * D * np.random.normal(0, 1, self.dim)
new_pos = self.best_position + step
else:
# 策略2: 后退探索 (向随机方向移动)
# X_i^{t+1} = X_{rand}^t + β * D * rand[0,1]
step = self.beta * D * np.random.rand(self.dim)
new_pos = X_rand_t + step
# 边界处理
new_pos = np.clip(new_pos, self.lb, self.ub)
return new_pos
def optimize(self):
"""执行超参数优化过程"""
for iter in range(self.max_iter):
for i in range(self.pop_size):
# 更新位置
new_pos = self.update_position(i, iter)
new_fit = self.obj_func(new_pos)
# 贪婪选择
if new_fit < self.fitness[i]:
self.positions[i] = new_pos
self.fitness[i] = new_fit
# 更新全局最优
if new_fit < self.best_fitness:
self.best_position = new_pos.copy()
self.best_fitness = new_fit
# 记录历史
self.history['best_fit'].append(self.best_fitness)
self.history['mean_fit'].append(np.mean(self.fitness))
# 动态调整策略阈值
self.p_threshold = 0.5 + 0.4 * (iter / self.max_iter)
# 每50代打印进度
if iter % 50 == 0:
print(f"Iteration {iter}/{self.max_iter}, Best Fitness: {self.best_fitness:.6f}")
return self.best_position, self.best_fitness
def plot_convergence(self):
"""绘制收敛曲线"""
plt.figure(figsize=(10, 6))
plt.semilogy(self.history['best_fit'], label='Best Fitness', lw=2)
plt.plot(self.history['mean_fit'], label='Mean Fitness', ls='--')
plt.xlabel('Iteration')
plt.ylabel('Fitness (Log Scale)')
plt.title('CPO Hyperparameter Optimization Convergence')
plt.legend()
plt.grid(True, which="both", ls="--")
plt.show()
# 示例: 将CPO集成到Scikit-learn超参数优化
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
# 加载数据集
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
def evaluate_rf_hyperparams(encoded_params):
"""
评估随机森林的超参数组合
encoded_params: 编码后的超参数向量 [n_estimators, max_depth, min_samples_split, min_samples_leaf]
"""
try:
# 解码超参数 (将优化空间映射到实际超参数范围)
params = {
'n_estimators': int(10 + 190 * (encoded_params[0] + 5) / 10), # 映射到[10, 200]
'max_depth': int(1 + 19 * (encoded_params[1] + 5) / 10), # 映射到[1, 20]
'min_samples_split': max(2, int(encoded_params[2] * 10)), # 映射到[2, 50]
'min_samples_leaf': max(1, int(encoded_params[3] * 5)), # 映射到[1, 25]
}
# 创建并训练模型
model = RandomForestClassifier(**params, random_state=42)
model.fit(X_train, y_train)
# 在验证集上评估
val_pred = model.predict(X_val)
accuracy = accuracy_score(y_val, val_pred)
# 返回需要最小化的损失 (1 - accuracy)
return 1 - accuracy
except Exception as e:
# 对于无效的超参数组合返回高损失
print(f"Invalid hyperparameters: {params}, Error: {str(e)}")
return 10.0 # 高损失值
if __name__ == "__main__":
# 超参数空间维度: 我们优化4个关键超参数
dim = 4
# 创建优化器
cpo = CrestedPorcupineOptimizer(
objective_func=evaluate_rf_hyperparams,
dim=dim,
pop_size=20,
max_iter=100,
lb=-5,
ub=5
)
print("Starting hyperparameter optimization for Random Forest...")
best_hyperparams, best_loss = cpo.optimize()
best_accuracy = 1 - best_loss
# 解码最佳超参数
best_params = {
'n_estimators': int(10 + 190 * (best_hyperparams[0] + 5) / 10),
'max_depth': int(1 + 19 * (best_hyperparams[1] + 5) / 10),
'min_samples_split': max(2, int(best_hyperparams[2] * 10)),
'min_samples_leaf': max(1, int(best_hyperparams[3] * 5)),
}
print("\nOptimization completed!")
print(f"Best hyperparameters found: {best_params}")
print(f"Best validation accuracy: {best_accuracy:.4f}")
# 绘制收敛曲线
cpo.plot_convergence()
# 使用最佳超参数训练最终模型
final_model = RandomForestClassifier(**best_params, random_state=42)
final_model.fit(X_train, y_train)
final_accuracy = accuracy_score(y_val, final_model.predict(X_val))
print(f"Final model validation accuracy: {final_accuracy:.4f}")
```
### 关键修正说明
1. **距离向量 `D` 的明确定义**:
```python
D = X_rand_t - X_i_t
```
- `D` 是**位置空间中的向量**,表示当前个体与随机个体在超参数空间中的差异
- 不是适应度值的差异,而是超参数组合之间的差异向量
- 每个维度对应一个超参数的差异
2. **超参数映射机制**:
```python
# 解码超参数
params = {
'n_estimators': int(10 + 190 * (encoded_params[0] + 5) / 10),
'max_depth': int(1 + 19 * (encoded_params[1] + 5) / 10),
# ...
}
```
- 将优化算法中的连续值映射到实际超参数范围
- 支持整数型超参数(如 n_estimators)和浮点型超参数
3. **适应度函数的定义**:
```python
def evaluate_rf_hyperparams(encoded_params):
# ...
return 1 - accuracy # 最小化损失
```
- 算法最小化目标函数,因此使用 `1 - accuracy` 作为损失
- 对于分类问题,也可以使用交叉熵损失等
4. **超参数搜索空间**:
```python
cpo = CrestedPorcupineOptimizer(..., lb=-5, ub=5)
```
- 定义统一的搜索空间 `[-5, 5]`
- 通过映射函数转换到实际超参数范围
### 距离向量的具体解释
在CPO算法中,距离向量 `D` 定义为:
$$D = X_{\text{rand}}^t - X_i^t$$
其中:
- $X_i^t$:当前个体的位置向量(当前超参数组合)
- $X_{\text{rand}}^t$:随机选择的个体位置向量(随机超参数组合)
**在超参数优化中的含义**:
1. 每个维度对应一个超参数的差值
2. 例如,对于学习率超参数:
- 如果 $X_{\text{rand}}^t$ 的学习率是 0.01
- $X_i^t$ 的学习率是 0.005
- 则该维度的 $D$ 值为 0.005
3. 算法利用这个差异向量:
- 在前进策略中:向当前最优解方向移动,但保持与随机个体的相对差异
- 在后退策略中:向随机个体方向移动,使用差异向量作为步长基础
### 实际应用建议
1. **超参数类型处理**:
```python
# 类别型超参数示例
if hyperparam_type == 'categorical':
# 使用整数表示类别
category_idx = int((encoded_value - lb) / (ub - lb) * num_categories)
actual_value = categories[category_idx]
```
2. **学习率自适应调整**:
```python
# 随迭代调整学习率
self.alpha = initial_alpha * (1 - iter / self.max_iter)
self.beta = initial_beta * (iter / self.max_iter)
```
3. **并行评估加速**:
```python
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor() as executor:
futures = [executor.submit(self.obj_func, pos) for pos in new_positions]
new_fitness = [f.result() for f in futures]
```