Selection in expected linear time

本文介绍了一个使用C#实现的快速选择算法,该算法能够从数组中找出第i小的元素,平均时间复杂度为O(n)。通过随机化分区过程提高效率。

下面的是用C#写的一个算法, 功能是从一个数组中选择第 i 小的一个数, 平均时间复杂度是Θ(n).

using System;

using System.Collections.Generic;

using System.Text;

using System.Diagnostics;

 

class Program

{

    
static void Main(string[] args)

    
{

        
int[] array = new int[] 93471051 };

        
int ismall = RandomizedSelect(array, 3);

        Debug.Assert(ismall 
== 4);

    }


 

    
/// <summary>

    
/// select the value of the i-th smallest number of the array

    
/// </summary>


    
static int RandomizedSelect(int[] array, int i)

    
{

        
return RandomizedSelect(array, 0, array.Length - 1, i);

    }


 

    
/// <summary>

    
/// select the value of the i-th smallest number between array[startIndex] and array[endIndex]

    
/// </summary>


    
static int RandomizedSelect(int[] array, int startIndex, int endIndex, int i)

    
{

        
if (startIndex == endIndex)

            
return array[startIndex];

        
int q = Partition(array, startIndex, endIndex);

        
int k = q - startIndex + 1;

        
if (k == i)

            
return array[q];

        
else if (k < i)

            
return RandomizedSelect(array, q + 1, endIndex, i - k);

        
else

            
return RandomizedSelect(array, startIndex, q - 1, i);

    }


 

    
/// <summary>

    
/// partition the array(smaller left, bigger right), return the pivot index

    
/// </summary>


    
static int Partition(int[] array, int startIndex, int endIndex)

    
{

        
//just using the middle number, thought it may not be the best way

        
int mid = (endIndex + startIndex) / 2;

        
int v = array[mid];

        Swap(array, mid, endIndex);

        
int i = startIndex - 1;

        
int j = endIndex;

        
while (true)

        
{

            
while (array[++i] < v) { }

            
while (array[--j] > v) { }

            
if (i > j)

                
break;

            Swap(array, i, j);

        }


        Swap(array, i, endIndex);

        
return i;

    }


 

    
/// <summary>

    
/// swap two numbers in the array

    
/// </summary>


    
static void Swap(int[] array, int index1, int index2)

    
{

        
int temp = array[index1];

        array[index1] 
= array[index2];

        array[index2] 
= temp;

    }


}

中文回复:此代码出现该问题,对其进行改正import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset, Subset from torch.optim.lr_scheduler import ReduceLROnPlateau import pandas as pd import numpy as np from scipy.optimize import differential_evolution import random import time from sklearn.model_selection import TimeSeriesSplit from datetime import datetime class OilTempLSTM(nn.Module): def __init__(self, input_size=15, hidden_size=128, output_size=1, dropout_rate=0.3): super(OilTempLSTM, self).__init__() # 第一层LSTM (保持2层结构) self.lstm1 = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=1, batch_first=True ) self.bn1 = nn.BatchNorm1d(hidden_size) # 第二层LSTM (保持2层结构) self.lstm2 = nn.LSTM( input_size=hidden_size, hidden_size=hidden_size//2, num_layers=1, batch_first=True ) self.bn2 = nn.BatchNorm1d(hidden_size//2) # 全连接层 self.fc1 = nn.Linear(hidden_size//2, 32) self.fc2 = nn.Linear(32, output_size) self.dropout = nn.Dropout(dropout_rate) def forward(self, x): # 第一层LSTM x, _ = self.lstm1(x) x = x.permute(0, 2, 1) x = self.bn1(x) x = x.permute(0, 2, 1) x = self.dropout(x) # 第二层LSTM x, _ = self.lstm2(x) x = x[:, -1, :] # 取最后时间步 x = self.bn2(x) x = self.dropout(x) # 全连接层 x = torch.relu(self.fc1(x)) x = self.fc2(x) return x def __str__(self): return f"""OilTempLSTM( (lstm1): LSTM(input_size={self.lstm1.input_size}, hidden_size={self.lstm1.hidden_size}, num_layers=1) (bn1): BatchNorm1d({self.bn1.num_features}) (lstm2): LSTM(input_size={self.lstm2.input_size}, hidden_size={self.lstm2.hidden_size}, num_layers=1) (bn2): BatchNorm1d({self.bn2.num_features}) (fc1): Linear(in_features={self.fc1.in_features}, out_features={self.fc1.out_features}) (fc2): Linear(in_features={self.fc2.in_features}, out_features={self.fc2.out_features}) (dropout): Dropout(p={self.dropout.p}) )""" def load_data(file_path, target_col, feature_cols, time_steps=18, train_ratio=0.8): """从Excel文件加载数据并创建时间序列数据集(时间步长固定为18)""" df = pd.read_excel(file_path) data = df[feature_cols + [target_col]].values.astype(np.float32) # 数据标准化 mean = data.mean(axis=0) std = data.std(axis=0) data = (data - mean) / std # 创建时间序列(固定18个时间步长) X, y = [], [] for i in range(len(data) - time_steps): X.append(data[i:i+time_steps, :]) y.append(data[i+time_steps, feature_cols.index(target_col)]) X = np.array(X) y = np.array(y) # 分割训练集和测试集 split_idx = int(len(X) * train_ratio) X_train, X_test = X[:split_idx], X[split_idx:] y_train, y_test = y[:split_idx], y[split_idx:] return X_train, y_train, X_test, y_test, mean, std def evaluate_model(params, X_train, y_train, X_test, y_test, device, sample_ratio=0.3): """评估模型性能,只使用部分样本""" hidden_size, dropout_rate, lr, batch_size, weight_decay = params # 参数范围约束 hidden_size = max(32, min(256, int(hidden_size))) # 32-256之间 dropout_rate = max(0.1, min(0.5, dropout_rate)) # 0.1-0.5之间 lr = max(1e-4, min(1e-2, lr)) # 1e-4到1e-2 batch_size = max(16, min(128, int(batch_size))) # 16-128之间 weight_decay = max(1e-6, min(1e-3, weight_decay)) # 1e-6到1e-3 # 随机选择部分样本用于快速评估 num_samples = int(len(X_train) * sample_ratio) indices = np.random.choice(len(X_train), num_samples, replace=False) X_train_sample = X_train[indices] y_train_sample = y_train[indices] # 创建数据加载器 train_dataset = TensorDataset(torch.FloatTensor(X_train_sample), torch.FloatTensor(y_train_sample)) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False) test_dataset = TensorDataset(torch.FloatTensor(X_test), torch.FloatTensor(y_test)) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False) # 初始化模型(保持2层结构) model = OilTempLSTM( input_size=X_train.shape[2], hidden_size=hidden_size, dropout_rate=dropout_rate ).to(device) criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay) scheduler = ReduceLROnPlateau(optimizer, 'min', patience=5) # 训练模型(快速评估) model.train() for epoch in range(30): # 使用较少epoch进行快速评估 total_loss = 0 for X_batch, y_batch in train_loader: X_batch, y_batch = X_batch.to(device), y_batch.to(device) optimizer.zero_grad() outputs = model(X_batch) loss = criterion(outputs.squeeze(), y_batch) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() total_loss += loss.item() scheduler.step(total_loss/len(train_loader)) # 在测试集上评估 model.eval() test_loss = 0 with torch.no_grad(): for X_batch, y_batch in test_loader: X_batch, y_batch = X_batch.to(device), y_batch.to(device) outputs = model(X_batch) test_loss += criterion(outputs.squeeze(), y_batch).item() return test_loss / len(test_loader) def differential_evolution_optimization(X_train, y_train, X_test, y_test, device, popsize=40, maxiter=20): """使用差分进化算法优化超参数""" # 定义参数边界 bounds = [ (64, 256), # hidden_size (整数) (0.1, 0.5), # dropout_rate (1e-4, 1e-2), # learning_rate (32, 128), # batch_size (整数) (1e-6, 1e-3) # weight_decay ] # 包装评估函数 def de_evaluate(params): # 将连续参数转换为合适的格式 params = [ int(params[0]), # hidden_size转为整数 params[1], # dropout_rate params[2], # learning_rate int(params[3]), # batch_size转为整数 params[4] # weight_decay ] return evaluate_model(params, X_train, y_train, X_test, y_test, device) print("Starting differential evolution optimization...") start_time = time.time() # 运行差分进化算法 result = differential_evolution( de_evaluate, bounds, strategy='best1bin', maxiter=maxiter, popsize=popsize, mutation=(0.5, 1.0), recombination=0.7, tol=0.01, seed=42 ) end_time = time.time() print(f"Optimization completed in {end_time - start_time:.2f} seconds") # 获取最佳参数 best_params = [ int(result.x[0]), # hidden_size result.x[1], # dropout_rate result.x[2], # learning_rate int(result.x[3]), # batch_size result.x[4] # weight_decay ] return best_params, result.fun def train_final_model(X_train, y_train, X_test, y_test, params, device, epochs=200): """使用最佳参数训练最终模型(保持2层结构)""" hidden_size, dropout_rate, lr, batch_size, weight_decay = params # 创建数据加载器 train_dataset = TensorDataset(torch.FloatTensor(X_train), torch.FloatTensor(y_train)) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False) test_dataset = TensorDataset(torch.FloatTensor(X_test), torch.FloatTensor(y_test)) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False) # 初始化模型(保持2层结构) model = OilTempLSTM( input_size=X_train.shape[2], hidden_size=hidden_size, dropout_rate=dropout_rate ).to(device) # 打印模型结构 print("\nFinal model architecture:") print(model) criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay) scheduler = ReduceLROnPlateau(optimizer, 'min', patience=10) best_loss = float('inf') best_model = None # 训练模型 for epoch in range(epochs): model.train() train_loss = 0 for X_batch, y_batch in train_loader: X_batch, y_batch = X_batch.to(device), y_batch.to(device) optimizer.zero_grad() outputs = model(X_batch) loss = criterion(outputs.squeeze(), y_batch) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() train_loss += loss.item() avg_train_loss = train_loss / len(train_loader) scheduler.step(avg_train_loss) # 验证集评估 model.eval() test_loss = 0 with torch.no_grad(): for X_batch, y_batch in test_loader: X_batch, y_batch = X_batch.to(device), y_batch.to(device) outputs = model(X_batch) test_loss += criterion(outputs.squeeze(), y_batch).item() avg_test_loss = test_loss / len(test_loader) if avg_test_loss < best_loss: best_loss = avg_test_loss best_model = model.state_dict() if epoch % 10 == 0: print(f'Epoch {epoch}, Train Loss: {avg_train_loss:.4f}, Test Loss: {avg_test_loss:.4f}') # 加载最佳模型 model.load_state_dict(best_model) return model, best_loss def time_series_cross_validation(X, y, params, device, n_splits=5, epochs=100): """时间序列交叉验证""" tscv = TimeSeriesSplit(n_splits=n_splits) fold_losses = [] print(f"\nStarting Time Series Cross Validation with {n_splits} folds...") for fold, (train_index, test_index) in enumerate(tscv.split(X)): print(f"\nFold {fold + 1}/{n_splits}") X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] # 训练模型 model, loss = train_final_model( X_train, y_train, X_test, y_test, params, device, epochs=epochs ) fold_losses.append(loss) print(f"Fold {fold + 1} completed with test loss: {loss:.4f}") print("\nCross Validation Results:") for i, loss in enumerate(fold_losses): print(f"Fold {i + 1}: {loss:.4f}") print(f"Average loss: {np.mean(fold_losses):.4f} ± {np.std(fold_losses):.4f}") return np.mean(fold_losses) if __name__ == "__main__": # 配置 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(f"Using device: {device}") # 数据加载 - 请替换为实际的文件路径和列名 file_path = 'C:/Users/Hyl/Desktop/undeadd.xlsx' target_col = '齿轮箱油池温度平均值' feature_cols = [ '齿轮箱高速轴轴承(风轮侧)温度平均值', '齿轮箱进油口温度平均值', '风向10s平均值', '齿轮箱入口压力平均值', '齿轮箱油泵压力平均值', '齿轮箱油池温度平均值', '发电机V相定子绕组温度平均值', '主轴轴承2(后轴承)温度平均值', '发电机前轴承温度平均值', '偏航系统压力平均值', '10m平均风速平均值', '发电机后轴承温度平均值', '环境温度平均值', '实际功率平均值', '累计应发电量平均值' ] # 特征列名 # 加载数据(时间步长固定为18) print("Loading data...") X_train, y_train, X_test, y_test, mean, std = load_data( file_path, target_col, feature_cols, time_steps=18 ) # 差分进化优化 best_params, best_loss = differential_evolution_optimization( X_train, y_train, X_test, y_test, device, popsize=40, # 种群规模设为40 maxiter=20 # 最大迭代次数 ) print("\nBest parameters found:") param_names = ["hidden_size", "dropout_rate", "learning_rate", "batch_size", "weight_decay"] for name, value in zip(param_names, best_params): print(f"{name}: {value}") print(f"Best validation loss: {best_loss:.4f}") # 使用最佳参数训练最终模型 print("\nTraining final model with best parameters...") final_model, final_loss = train_final_model( X_train, y_train, X_test, y_test, best_params, device, epochs=200 ) # 时间序列交叉验证 X_full = np.concatenate((X_train, X_test)) y_full = np.concatenate((y_train, y_test)) cv_loss = time_series_cross_validation(X_full, y_full, best_params, device) # 保存模型 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") model_path = f'oil_temp_lstm_de_optimized_{timestamp}.pth' torch.save({ 'model_state_dict': final_model.state_dict(), 'mean': mean, 'std': std, 'params': best_params, 'loss': final_loss, 'cv_loss': cv_loss }, model_path) print(f"\nModel saved to {model_path}")日志全称为:--------------------------------------------------------------------------- ValueError Traceback (most recent call last) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\scipy\optimize\_differentialevolution.py:1319, in DifferentialEvolutionSolver._calculate_population_energies(self, population) 1318 try: -> 1319 calc_energies = list( 1320 self._mapwrapper(self.func, parameters_pop[0:S]) 1321 ) 1322 calc_energies = np.squeeze(calc_energies) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\scipy\_lib\_util.py:441, in _FunctionWrapper.__call__(self, x) 440 def __call__(self, x): --> 441 return self.f(x, *self.args) Cell In[3], line 181, in differential_evolution_optimization.<locals>.de_evaluate(params) 174 params = [ 175 int(params[0]), # hidden_size转为整数 176 params[1], # dropout_rate (...) 179 params[4] # weight_decay 180 ] --> 181 return evaluate_model(params, X_train, y_train, X_test, y_test, device) Cell In[3], line 139, in evaluate_model(params, X_train, y_train, X_test, y_test, device, sample_ratio) 138 optimizer.zero_grad() --> 139 outputs = model(X_batch) 140 loss = criterion(outputs.squeeze(), y_batch) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\torch\nn\modules\module.py:1751, in Module._wrapped_call_impl(self, *args, **kwargs) 1750 else: -> 1751 return self._call_impl(*args, **kwargs) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\torch\nn\modules\module.py:1762, in Module._call_impl(self, *args, **kwargs) 1759 if not (self._backward_hooks or self._backward_pre_hooks or self._forward_hooks or self._forward_pre_hooks 1760 or _global_backward_pre_hooks or _global_backward_hooks 1761 or _global_forward_hooks or _global_forward_pre_hooks): -> 1762 return forward_call(*args, **kwargs) 1764 result = None Cell In[3], line 51, in OilTempLSTM.forward(self, x) 50 x = x[:, -1, :] # 取最后时间步 ---> 51 x = self.bn2(x) 52 x = self.dropout(x) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\torch\nn\modules\module.py:1751, in Module._wrapped_call_impl(self, *args, **kwargs) 1750 else: -> 1751 return self._call_impl(*args, **kwargs) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\torch\nn\modules\module.py:1762, in Module._call_impl(self, *args, **kwargs) 1759 if not (self._backward_hooks or self._backward_pre_hooks or self._forward_hooks or self._forward_pre_hooks 1760 or _global_backward_pre_hooks or _global_backward_hooks 1761 or _global_forward_hooks or _global_forward_pre_hooks): -> 1762 return forward_call(*args, **kwargs) 1764 result = None File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\torch\nn\modules\batchnorm.py:193, in _BatchNorm.forward(self, input) 188 r""" 189 Buffers are only updated if they are to be tracked and we are in training mode. Thus they only need to be 190 passed when the update should occur (i.e. in training mode when they are tracked), or when buffer stats are 191 used for normalization (i.e. in eval mode when buffers are not None). 192 """ --> 193 return F.batch_norm( 194 input, 195 # If buffers are not to be tracked, ensure that they won't be updated 196 self.running_mean 197 if not self.training or self.track_running_stats 198 else None, 199 self.running_var if not self.training or self.track_running_stats else None, 200 self.weight, 201 self.bias, 202 bn_training, 203 exponential_average_factor, 204 self.eps, 205 ) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\torch\nn\functional.py:2820, in batch_norm(input, running_mean, running_var, weight, bias, training, momentum, eps) 2819 if training: -> 2820 _verify_batch_size(input.size()) 2822 return torch.batch_norm( 2823 input, 2824 weight, (...) 2831 torch.backends.cudnn.enabled, 2832 ) File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\torch\nn\functional.py:2786, in _verify_batch_size(size) 2785 if size_prods == 1: -> 2786 raise ValueError( 2787 f"Expected more than 1 value per channel when training, got input size {size}" 2788 ) ValueError: Expected more than 1 value per channel when training, got input size torch.Size([1, 41]) The above exception was the direct cause of the following exception: RuntimeError Traceback (most recent call last) Cell In[3], line 335 330 X_train, y_train, X_test, y_test, mean, std = load_data( 331 file_path, target_col, feature_cols, time_steps=18 332 ) 334 # 差分进化优化 --> 335 best_params, best_loss = differential_evolution_optimization( 336 X_train, y_train, X_test, y_test, device, 337 popsize=40, # 种群规模设为40 338 maxiter=20 # 最大迭代次数 339 ) 341 print("\nBest parameters found:") 342 param_names = ["hidden_size", "dropout_rate", "learning_rate", "batch_size", "weight_decay"] Cell In[3], line 187, in differential_evolution_optimization(X_train, y_train, X_test, y_test, device, popsize, maxiter) 184 start_time = time.time() 186 # 运行差分进化算法 --> 187 result = differential_evolution( 188 de_evaluate, 189 bounds, 190 strategy='best1bin', 191 maxiter=maxiter, 192 popsize=popsize, 193 mutation=(0.5, 1.0), 194 recombination=0.7, 195 tol=0.01, 196 seed=42 197 ) 199 end_time = time.time() 200 print(f"Optimization completed in {end_time - start_time:.2f} seconds") File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\scipy\optimize\_differentialevolution.py:503, in differential_evolution(func, bounds, args, strategy, maxiter, popsize, tol, mutation, recombination, seed, callback, disp, polish, init, atol, updating, workers, constraints, x0, integrality, vectorized) 486 # using a context manager means that any created Pool objects are 487 # cleared up. 488 with DifferentialEvolutionSolver(func, bounds, args=args, 489 strategy=strategy, 490 maxiter=maxiter, (...) 501 integrality=integrality, 502 vectorized=vectorized) as solver: --> 503 ret = solver.solve() 505 return ret File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\scipy\optimize\_differentialevolution.py:1159, in DifferentialEvolutionSolver.solve(self) 1154 self.feasible, self.constraint_violation = ( 1155 self._calculate_population_feasibilities(self.population)) 1157 # only work out population energies for feasible solutions 1158 self.population_energies[self.feasible] = ( -> 1159 self._calculate_population_energies( 1160 self.population[self.feasible])) 1162 self._promote_lowest_energy() 1164 # do the optimization. File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\scipy\optimize\_differentialevolution.py:1326, in DifferentialEvolutionSolver._calculate_population_energies(self, population) 1322 calc_energies = np.squeeze(calc_energies) 1323 except (TypeError, ValueError) as e: 1324 # wrong number of arguments for _mapwrapper 1325 # or wrong length returned from the mapper -> 1326 raise RuntimeError( 1327 "The map-like callable must be of the form f(func, iterable), " 1328 "returning a sequence of numbers the same length as 'iterable'" 1329 ) from e 1331 if calc_energies.size != S: 1332 if self.vectorized: RuntimeError: The map-like callable must be of the form f(func, iterable), returning a sequence of numbers the same length as 'iterable'
最新发布
06-11
根据你提供的错误日志,问题的根本原因在于 `scipy.optimize.differential_evolution` 的评估函数返回值不符合预期。具体来说,`differential_evolution` 要求评估函数返回一个与输入参数长度相同的序列(或标量),而你的代码中可能存在以下问题: 1. **BatchNorm 层的输入问题**: - 在 LSTM 的第二层后,`self.bn2(x)` 被调用时,输入 `x` 的形状可能不满足 BatchNorm 的要求。BatchNorm 需要每个通道至少有两个样本,而你的数据可能是单样本(batch size = 1)。 2. **差分进化算法的返回值问题**: - 差分进化算法要求评估函数返回一个标量值,但如果你的评估函数返回了非标量值,可能会导致错误。 --- ### 解决方案 #### 1. **修正 BatchNorm 输入问题** - 当 batch size 为 1 时,BatchNorm 层会报错,因为需要至少两个样本来进行归一化。 - 解决方法是:在训练过程中,确保 batch size 不为 1,或者在模型中避免使用 BatchNorm 层。 #### 2. **修正差分进化算法的返回值问题** - 确保 `evaluate_model` 函数返回的是一个标量值(即测试集上的平均损失)。 以下是修正后的代码: ```python import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset from torch.optim.lr_scheduler import ReduceLROnPlateau import pandas as pd import numpy as np from scipy.optimize import differential_evolution import random import time from sklearn.model_selection import TimeSeriesSplit from datetime import datetime class OilTempLSTM(nn.Module): def __init__(self, input_size=15, hidden_size=128, output_size=1, dropout_rate=0.3): super(OilTempLSTM, self).__init__() # 第一层LSTM self.lstm1 = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=1, batch_first=True ) # 第二层LSTM self.lstm2 = nn.LSTM( input_size=hidden_size, hidden_size=hidden_size // 2, num_layers=1, batch_first=True ) # 全连接层 self.fc1 = nn.Linear(hidden_size // 2, 32) self.fc2 = nn.Linear(32, output_size) self.dropout = nn.Dropout(dropout_rate) def forward(self, x): # 第一层LSTM x, _ = self.lstm1(x) x = x[:, -1, :] # 取最后时间步 # 第二层LSTM x, _ = self.lstm2(x.unsqueeze(1)) # 添加一个时间步维度 x = x[:, -1, :] # 取最后时间步 x = self.dropout(x) # 全连接层 x = torch.relu(self.fc1(x)) x = self.fc2(x) return x def load_data(file_path, target_col, feature_cols, time_steps=18, train_ratio=0.8): """从Excel文件加载数据并创建时间序列数据集""" df = pd.read_excel(file_path) data = df[feature_cols + [target_col]].values.astype(np.float32) # 数据标准化 mean = data.mean(axis=0) std = data.std(axis=0) data = (data - mean) / std # 创建时间序列 X, y = [], [] for i in range(len(data) - time_steps): X.append(data[i:i + time_steps, :]) y.append(data[i + time_steps, feature_cols.index(target_col)]) X = np.array(X) y = np.array(y) # 分割训练集和测试集 split_idx = int(len(X) * train_ratio) X_train, X_test = X[:split_idx], X[split_idx:] y_train, y_test = y[:split_idx], y[split_idx:] return X_train, y_train, X_test, y_test, mean, std def evaluate_model(params, X_train, y_train, X_test, y_test, device, sample_ratio=0.3): """评估模型性能,只使用部分样本""" hidden_size, dropout_rate, lr, batch_size, weight_decay = params # 参数范围约束 hidden_size = max(32, min(256, int(hidden_size))) # 32-256之间 dropout_rate = max(0.1, min(0.5, dropout_rate)) # 0.1-0.5之间 lr = max(1e-4, min(1e-2, lr)) # 1e-4到1e-2 batch_size = max(16, min(128, int(batch_size))) # 16-128之间 weight_decay = max(1e-6, min(1e-3, weight_decay)) # 1e-6到1e-3 # 随机选择部分样本用于快速评估 num_samples = int(len(X_train) * sample_ratio) indices = np.random.choice(len(X_train), num_samples, replace=False) X_train_sample = X_train[indices] y_train_sample = y_train[indices] # 创建数据加载器 train_dataset = TensorDataset(torch.FloatTensor(X_train_sample), torch.FloatTensor(y_train_sample)) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False) test_dataset = TensorDataset(torch.FloatTensor(X_test), torch.FloatTensor(y_test)) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False) # 初始化模型 model = OilTempLSTM( input_size=X_train.shape[2], hidden_size=hidden_size, dropout_rate=dropout_rate ).to(device) criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay) scheduler = ReduceLROnPlateau(optimizer, 'min', patience=5) # 训练模型(快速评估) model.train() for epoch in range(30): # 使用较少epoch进行快速评估 total_loss = 0 for X_batch, y_batch in train_loader: if len(X_batch) == 1: # 跳过 batch size 为 1 的情况 continue X_batch, y_batch = X_batch.to(device), y_batch.to(device) optimizer.zero_grad() outputs = model(X_batch) loss = criterion(outputs.squeeze(), y_batch) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() total_loss += loss.item() scheduler.step(total_loss / len(train_loader)) # 在测试集上评估 model.eval() test_loss = 0 with torch.no_grad(): for X_batch, y_batch in test_loader: if len(X_batch) == 1: # 跳过 batch size 为 1 的情况 continue X_batch, y_batch = X_batch.to(device), y_batch.to(device) outputs = model(X_batch) test_loss += criterion(outputs.squeeze(), y_batch).item() return test_loss / len(test_loader) def differential_evolution_optimization(X_train, y_train, X_test, y_test, device, popsize=40, maxiter=20): """使用差分进化算法优化超参数""" # 定义参数边界 bounds = [ (64, 256), # hidden_size (整数) (0.1, 0.5), # dropout_rate (1e-4, 1e-2), # learning_rate (32, 128), # batch_size (整数) (1e-6, 1e-3) # weight_decay ] # 包装评估函数 def de_evaluate(params): # 将连续参数转换为合适的格式 params = [ int(params[0]), # hidden_size转为整数 params[1], # dropout_rate params[2], # learning_rate int(params[3]), # batch_size转为整数 params[4] # weight_decay ] return evaluate_model(params, X_train, y_train, X_test, y_test, device) print("Starting differential evolution optimization...") start_time = time.time() # 运行差分进化算法 result = differential_evolution( de_evaluate, bounds, strategy='best1bin', maxiter=maxiter, popsize=popsize, mutation=(0.5, 1.0), recombination=0.7, tol=0.01, seed=42 ) end_time = time.time() print(f"Optimization completed in {end_time - start_time:.2f} seconds") # 获取最佳参数 best_params = [ int(result.x[0]), # hidden_size result.x[1], # dropout_rate result.x[2], # learning_rate int(result.x[3]), # batch_size result.x[4] # weight_decay ] return best_params, result.fun if __name__ == "__main__": # 配置 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(f"Using device: {device}") # 数据加载 - 请替换为实际的文件路径和列名 file_path = 'C:/Users/Hyl/Desktop/undeadd.xlsx' target_col = '齿轮箱油池温度平均值' feature_cols = [ '齿轮箱高速轴轴承(风轮侧)温度平均值', '齿轮箱进油口温度平均值', '风向10s平均值', '齿轮箱入口压力平均值', '齿轮箱油泵压力平均值', '齿轮箱油池温度平均值', '发电机V相定子绕组温度平均值', '主轴轴承2(后轴承)温度平均值', '发电机前轴承温度平均值', '偏航系统压力平均值', '10m平均风速平均值', '发电机后轴承温度平均值', '环境温度平均值', '实际功率平均值', '累计应发电量平均值' ] # 特征列名 # 加载数据 X_train, y_train, X_test, y_test, mean, std = load_data( file_path, target_col, feature_cols, time_steps=18 ) # 差分进化优化 best_params, best_loss = differential_evolution_optimization( X_train, y_train, X_test, y_test, device, popsize=40, # 种群规模设为40 maxiter=20 # 最大迭代次数 ) print("\nBest parameters found:") param_names = ["hidden_size", "dropout_rate", "learning_rate", "batch_size", "weight_decay"] for name, value in zip(param_names, best_params): print(f"{name}: {value}") print(f"Best validation loss: {best_loss:.4f}") ``` --- ### 解释 #### 1. **BatchNorm 替代** - 原始代码中使用了 `BatchNorm1d`,但当 batch size 为 1 时会导致错误。因此,我们移除了 BatchNorm 层,并直接使用 Dropout 来正则化模型。 #### 2. **跳过单样本批次** - 在训练和测试过程中,如果某个批次的样本数为 1,则跳过该批次以避免 BatchNorm 报错。 #### 3. **差分进化算法** - 确保 `evaluate_model` 函数返回的是一个标量值(测试集上的平均损失)。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值