{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"}],"dockerImageVersionId":30635,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### This notebook is study from <a href=\"https://www.kaggle.com/code/yorkyong/exploring-eeg-a-beginner-s-guide\">exploring eeg a beginner's guide</a>.This notebook is training lightgbm models.","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"### Import necessary libraries.","metadata":{}},{"cell_type":"code","source":"import os#与操作系统进行交互的库\nimport pandas as pd#导入csv文件的库\nimport numpy as np#进行矩阵运算的库\nimport matplotlib.pyplot as plt#一个强大的绘图库\nfrom lightgbm import LGBMClassifier#导入lgbm分类器\nimport gc#垃圾回收的库\nimport dill#对对象进行序列化和反序列化(例如保存和加载树模型)\nfrom sklearn.model_selection import GroupKFold#根据数据的一些属性,例如地区,时间来划分K个子集\nimport warnings#避免一些可以忽略的报错\nwarnings.filterwarnings('ignore')#filterwarnings()方法是用于设置警告过滤器的方法，它可以控制警告信息的输出方式和级别。","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Config","metadata":{}},{"cell_type":"code","source":"#设置随机种子,保证模型可以复现\nimport random\nseed=2024\nnum_folds=10\n#这是pss4e1的参数,HMS比赛没有找过参数\nlgb_params={'random_state': seed, 'n_estimators': 1024,\n           'reg_alpha': 0.3245237982823759, 'reg_lambda': 9.713500590822735,\n           'colsample_bytree': 0.5031339908309955, 'subsample': 0.9680254188045883, \n           'learning_rate': 0.036537966896644465, 'num_leaves': 29, 'min_child_samples': 99}\n#设置随机种子,保证模型可以复现\nnp.random.seed(seed)\nrandom.seed(seed)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Import train and spectrogram dataset.","metadata":{}},{"cell_type":"code","source":"#读取数据\ntrain_df = pd.read_csv('/kaggle/input/hms-harmful-brain-activity-classification/train.csv')\nTARGETS = train_df.columns[-6:]#需要预测的列名\nprint('Train shape:', train_df.shape )\nprint('Targets', list(TARGETS))\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#将数据取出\nfiles = os.listdir('/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms/')\nspectrograms = {}\nfor i,f in enumerate(files):\n    tmp = pd.read_parquet(f\"/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms/{f}\")\n    #f:'1000086677.parquet'  \n    name = int(f.split('.')[0])#name:1000086677\n    spectrograms[name] = tmp.iloc[:,1:].values#第0列是时间列.shape:(T,400)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Feature engineer","metadata":{}},{"cell_type":"code","source":"#从训练数据中每个eeg_id 选出'spectrograd_id'第一个,并且'spectrogram_label_offset_seconds'最小的那个 \ntrain = train_df.groupby('eeg_id')[['spectrogram_id',\n                                    'spectrogram_label_offset_seconds',\n                                     'patient_id',\n                                     'expert_consensus'\n                                   ]].agg(\n    {'spectrogram_id':'first','spectrogram_label_offset_seconds':['min','max'],\n     'patient_id':'first','expert_consensus':'first'})\ntrain.columns = ['spec_id','min','max','patient_id','target']#将列名改成spec_id和min,'max'\n\n#每个eeg_id应该都有一个spec_id,但是每个spec_id却不一定只有一个eeg_id.\nprint(f\"len(train):{len(train)},unique_eeg:{train_df['eeg_id'].nunique()},unique_spec:{train['spec_id'].nunique()}\")\n\n#这里对每个样本专家投票的数据进行归一化\ntmp = train_df.groupby('eeg_id')[TARGETS].agg('sum')\nfor t in TARGETS:\n    train[t] = tmp[t].values\ny_data = train[TARGETS].values \ny_data = y_data / y_data.sum(axis=1,keepdims=True)\ntrain[TARGETS] = y_data\n\n#将eeg_id设置为索引\ntrain = train.reset_index() \n\ntrain.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#除了时间列,其他列都要\nSPEC_COLS = pd.read_parquet(f\"/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms/1000086677.parquet\").columns[1:]\n#对这些列的数据构造特征.\nFEATURES = [f'{c}_mean_10m' for c in SPEC_COLS]\nFEATURES += [f'{c}_min_10m' for c in SPEC_COLS]\nFEATURES += [f'{c}_mean_20s' for c in SPEC_COLS]\nFEATURES += [f'{c}_min_20s' for c in SPEC_COLS]\n\n#这个就是训练数据\ndata = np.zeros((len(train),len(FEATURES)))\nfor k in range(len(train)):\n    row = train.iloc[k]#取出第k个数据,或者说第K行\n    r = int( (row['min'] + row['max'])//4 ) \n    #找出对应的数据spectrograms[row.spec_id]\n    row_data=spectrograms[row.spec_id]\n    #数据 时间维度是[r:r+300],列是400,按列对不是缺失值的数据求均值和最小值.\n    data[k,:400] = np.nanmean(row_data[r:r+300,:],axis=0)\n    data[k,400:800] = np.nanmin(row_data[r:r+300,:],axis=0)\n    #数据 时间维度是[r+145:r+155],列是400,按列对不是缺失值的数据求均值和最小值.\n    data[k,800:1200] =  np.nanmean(row_data[r+145:r+155,:],axis=0)\n    data[k,1200:1600] = np.nanmin(row_data[r+145:r+155,:],axis=0)\n#统计好特征.\ntrain[FEATURES] = data\nprint('New train shape:',train.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Models training and Save","metadata":{}},{"cell_type":"code","source":"all_oof = []\nall_true = []\nTARS = {'Seizure':0, 'LPD':1, 'GPD':2, 'LRDA':3, 'GRDA':4, 'Other':5}\n\n#保存训练好的树模型,obj是保存的模型,path是需要保存的路径\ndef pickle_dump(obj, path):\n    #打开指定的路径path,binary write(二进制写入)\n    with open(path, mode=\"wb\") as f:\n        #将obj对象保存到f,使用协议版本4进行序列化\n        dill.dump(obj, f, protocol=4)\n#根据数据的一些属性,使用5折交叉验证.\ngkf = GroupKFold(n_splits=num_folds)\n#根据训练数据的类别和患者id来划分.\nfor fold, (train_index, valid_index) in enumerate(gkf.split(train , train .target, train .patient_id)):   \n    \n    print(f'Fold {fold+1}')\n    \n    model = LGBMClassifier(**lgb_params)\n    \n    # Prepare training and validation data\n    X_train = train.loc[train_index, FEATURES]\n    y_train = train.loc[train_index, 'target'].map(TARS)\n    X_valid = train.loc[valid_index, FEATURES]\n    y_valid = train.loc[valid_index, 'target'].map(TARS)\n    \n    #取出训练数据TARGETS这几列概率最大的概率值,也就是真实类别的概率值作为权重.\n    sample_weight=np.max(train.loc[train_index, TARGETS].values,axis=1)\n    \n    model.fit(X_train, y_train, eval_set=[(X_valid, y_valid)],\n              verbose=0,#不输出任何东西\n              sample_weight=sample_weight,\n              early_stopping_rounds=100)\n    pickle_dump(model, f'/kaggle/working/lgb_f{fold}.model') #保存训练好的模型   \n    \n    all_oof.append(model.predict_proba(X_valid))\n    all_true.append(train.loc[valid_index, TARGETS].values)\n    \n    del X_train, y_train, X_valid, y_valid#每次训练完删除掉\n    gc.collect()#如果一个对象不再引用,那就会被回收掉\n    \nall_oof = np.concatenate(all_oof)\nall_true = np.concatenate(all_true)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Metric","metadata":{}},{"cell_type":"code","source":"def KL_loss(p,q):\n    epsilon=10**(-15)\n    p=np.clip(p,epsilon,1-epsilon)\n    #对第一个维度,就是num_classes维度的损失求和,得到每个样本的损失,然后对第0维求平均,得到每个样本平均KL散度.\n    return np.mean(np.sum(p*(np.log(p)-np.log(q)),axis=1))\nprint(f\"CV of Kullback Leibler Divergence:{KL_loss(all_true,all_oof)}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}