1212from sklearn .metrics import accuracy_score , precision_score , recall_score , f1_score , roc_auc_score
1313from pathlib import Path
1414from datetime import datetime
15+ import glob
16+ import os
1517
1618logger = logging .getLogger (__name__ )
1719
@@ -96,24 +98,26 @@ def get_model_info(self) -> Dict[str, Any]:
9698class NeuralNetworkTrainer :
9799 """神经网络训练器"""
98100
99- def __init__ (self , model_save_path : str = "models/neural_rerank_model.pth" , max_history_size : int = 100 ):
101+ def __init__ (self , model_save_path : str = "models/neural_rerank_model.pth" , max_history_size : int = 100 , max_checkpoints : int = 3 ):
100102 """
101103 初始化训练器
102104
103105 Args:
104106 model_save_path: 模型保存路径
105107 max_history_size: 训练历史记录最大保存数量
108+ max_checkpoints: 最大检查点文件保留数量
106109 """
107110 self .model_save_path = model_save_path
108111 self .model = None
109112 self .device = torch .device ('cuda' if torch .cuda .is_available () else 'cpu' )
110113 self .training_history = []
111114 self .max_history_size = max_history_size
115+ self .max_checkpoints = max_checkpoints
112116
113117 # 确保模型目录存在
114118 Path (self .model_save_path ).parent .mkdir (parents = True , exist_ok = True )
115119
116- logger .info (f"神经网络训练器初始化完成,设备: { self .device } , 模型保存路径: { model_save_path } " )
120+ logger .info (f"神经网络训练器初始化完成,设备: { self .device } , 模型保存路径: { model_save_path } , 最大检查点保留数: { max_checkpoints } " )
117121
118122 def train_model (self ,
119123 features : List [List [float ]],
@@ -245,6 +249,9 @@ def train_model(self,
245249 # 加载最佳模型
246250 self ._load_checkpoint ()
247251
252+ # 训练完成后清理检查点文件
253+ self ._cleanup_checkpoints ()
254+
248255 # 评估模型
249256 train_metrics = self ._evaluate_model (X_train , y_train , "训练集" )
250257 val_metrics = self ._evaluate_model (X_val , y_val , "验证集" )
@@ -318,15 +325,21 @@ def _calculate_auc(self, y_true: np.ndarray, y_scores: np.ndarray) -> float:
318325 def _save_checkpoint (self ):
319326 """保存检查点"""
320327 if self .model is not None :
321- checkpoint_path = self .model_save_path .replace ('.pth' , '_checkpoint.pth' )
328+ timestamp = datetime .now ().strftime ("%Y%m%d_%H%M%S" )
329+ checkpoint_path = self .model_save_path .replace ('.pth' , f'_checkpoint_{ timestamp } .pth' )
322330 torch .save (self .model .state_dict (), checkpoint_path )
331+
332+ # 清理旧的检查点文件
333+ self ._cleanup_checkpoints ()
323334
324335 def _load_checkpoint (self ):
325- """加载检查点 """
336+ """加载最新的检查点 """
326337 if self .model is not None :
327- checkpoint_path = self .model_save_path .replace ('.pth' , '_checkpoint.pth' )
328- if Path (checkpoint_path ).exists ():
329- self .model .load_state_dict (torch .load (checkpoint_path , map_location = self .device ))
338+ # 查找最新的检查点文件
339+ latest_checkpoint = self ._find_latest_checkpoint ()
340+ if latest_checkpoint and Path (latest_checkpoint ).exists ():
341+ self .model .load_state_dict (torch .load (latest_checkpoint , map_location = self .device ))
342+ logger .debug (f"已加载检查点: { latest_checkpoint } " )
330343
331344 def _save_model (self ):
332345 """保存模型"""
@@ -384,6 +397,9 @@ def load_model(self, model_path: Optional[str] = None) -> bool:
384397 self .training_history = model_data .get ('training_history' , [])
385398 self ._cleanup_training_history ()
386399
400+ # 加载模型后清理旧的检查点文件
401+ self ._cleanup_checkpoints ()
402+
387403 logger .info (f"模型加载成功: { path } " )
388404 return True
389405
@@ -531,4 +547,115 @@ def retrain_with_new_data(self, features: List[List[float]], labels: List[int],
531547 except Exception as e :
532548 logger .error (f"增量训练失败: { e } " )
533549 raise
550+
551+ def _find_latest_checkpoint (self ) -> Optional [str ]:
552+ """
553+ 查找最新的检查点文件
554+
555+ Returns:
556+ 最新检查点文件的路径,如果没有则返回None
557+ """
558+ try :
559+ model_dir = Path (self .model_save_path ).parent
560+ model_name = Path (self .model_save_path ).stem
561+
562+ # 查找所有检查点文件
563+ checkpoint_pattern = str (model_dir / f"{ model_name } _checkpoint_*.pth" )
564+ checkpoint_files = glob .glob (checkpoint_pattern )
565+
566+ if not checkpoint_files :
567+ return None
568+
569+ # 按时间戳排序,返回最新的
570+ checkpoint_files .sort ()
571+ return checkpoint_files [- 1 ]
572+
573+ except Exception as e :
574+ logger .error (f"查找最新检查点失败: { e } " )
575+ return None
576+
577+ def _cleanup_checkpoints (self ):
578+ """
579+ 清理旧的检查点文件,只保留最近的N个检查点
580+ """
581+ try :
582+ model_dir = Path (self .model_save_path ).parent
583+ model_name = Path (self .model_save_path ).stem
584+
585+ # 查找所有检查点文件
586+ checkpoint_pattern = str (model_dir / f"{ model_name } _checkpoint_*.pth" )
587+ checkpoint_files = glob .glob (checkpoint_pattern )
588+
589+ if len (checkpoint_files ) <= self .max_checkpoints :
590+ return
591+
592+ # 按修改时间排序
593+ checkpoint_files .sort (key = lambda x : os .path .getmtime (x ))
594+
595+ # 删除多余的旧检查点文件
596+ files_to_delete = checkpoint_files [:- self .max_checkpoints ]
597+ deleted_count = 0
598+
599+ for file_path in files_to_delete :
600+ try :
601+ os .remove (file_path )
602+ deleted_count += 1
603+ logger .debug (f"删除旧检查点文件: { file_path } " )
604+ except Exception as e :
605+ logger .warning (f"删除检查点文件失败: { file_path } , 错误: { e } " )
606+
607+ if deleted_count > 0 :
608+ logger .info (f"检查点清理完成:删除了 { deleted_count } 个旧文件,保留最近 { len (checkpoint_files ) - deleted_count } 个检查点" )
609+
610+ except Exception as e :
611+ logger .error (f"检查点清理失败: { e } " )
612+
613+ def get_checkpoint_info (self ) -> Dict [str , Any ]:
614+ """
615+ 获取检查点文件信息
616+
617+ Returns:
618+ 检查点信息字典
619+ """
620+ try :
621+ model_dir = Path (self .model_save_path ).parent
622+ model_name = Path (self .model_save_path ).stem
623+
624+ # 查找所有检查点文件
625+ checkpoint_pattern = str (model_dir / f"{ model_name } _checkpoint_*.pth" )
626+ checkpoint_files = glob .glob (checkpoint_pattern )
627+
628+ checkpoint_info = []
629+ total_size = 0
630+
631+ for file_path in checkpoint_files :
632+ file_stats = os .stat (file_path )
633+ checkpoint_info .append ({
634+ 'path' : file_path ,
635+ 'size_mb' : file_stats .st_size / (1024 * 1024 ),
636+ 'modified_time' : datetime .fromtimestamp (file_stats .st_mtime ).isoformat ()
637+ })
638+ total_size += file_stats .st_size
639+
640+ # 按修改时间排序
641+ checkpoint_info .sort (key = lambda x : x ['modified_time' ])
642+
643+ return {
644+ 'checkpoint_count' : len (checkpoint_files ),
645+ 'max_checkpoints' : self .max_checkpoints ,
646+ 'total_size_mb' : total_size / (1024 * 1024 ),
647+ 'checkpoints' : checkpoint_info ,
648+ 'latest_checkpoint' : checkpoint_info [- 1 ]['path' ] if checkpoint_info else None
649+ }
650+
651+ except Exception as e :
652+ logger .error (f"获取检查点信息失败: { e } " )
653+ return {
654+ 'checkpoint_count' : 0 ,
655+ 'max_checkpoints' : self .max_checkpoints ,
656+ 'total_size_mb' : 0 ,
657+ 'checkpoints' : [],
658+ 'latest_checkpoint' : None ,
659+ 'error' : str (e )
660+ }
534661
0 commit comments