前段時間在參加比賽,發現有一些比賽上公開的代碼,其中的數據預處理步驟值得我們參考。
平常我們見到的都是數據預處理,現在我們來講一下特征工程跟數據預處理的區別。
-
數據預處理是指對原始數據進行清洗、轉換、縮放等操作,以便為后續的建模或分析任務做準備。這包括處理缺失值、異常值、重復值,以及對數據進行歸一化、標準化等操作,使數據適合模型處理。
-
特征工程則更側重于從原始數據中提取、構建或轉換特征,以提高模型的性能。這包括特征選擇、特征抽取、特征轉換等過程。在特征工程中,可以創建新的特征、組合現有特征、進行降維等操作,以便使模型更好地捕捉數據中的模式和關系
?訓練集
測試集
注:這個代碼也可以用在自己的工程項目中,還是比較不錯的!也是目前在Kaggle社區里公開代碼分數比較高的一個單模型。
1、安裝庫
!pip install rdkit
!pip install -U /kaggle/input/lightning-2-2-1/lightning-2.2.1-py3-none-any.whl
RDKit:是一個用于化學信息學和藥物發現的開源軟件包。它提供了豐富的化學信息處理功能和工具,用于分子建模、藥物設計、化合物篩選等領域。
PyTorch Lightning:于簡化和加速深度學習項目開發的庫。它構建在PyTorch之上,提供了高級抽象和預定義模板,使得構建、訓練和調試神經網絡模型變得更加簡單和高效。
2、導入所需的Python庫和模塊
from pathlib import Path
import numpy as np
import polars as pl
from sklearn.model_selection import train_test_split
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torchmetrics import AveragePrecision
import lightning as L
from lightning.pytorch.callbacks import (EarlyStopping,ModelCheckpoint,TQDMProgressBar,
)
from transformers import AutoConfig, AutoTokenizer, AutoModel, DataCollatorWithPadding
import datasets
from rdkit import Chem
?導入數據處理、深度學習、Lightning等相關模塊
3、配置超參數?
DEBUG = False
NORMALIZE = True
N_ROWS = 180_000_000
assert N_ROWS is None or N_ROWS % 3 == 0
if DEBUG:N_SAMPLES = 10_000
else:N_SAMPLES = 2_000_000
PROTEIN_NAMES = ["BRD4", "HSA", "sEH"]
data_dir = Path("/kaggle/input/leash-BELKA")
model_name = "DeepChem/ChemBERTa-10M-MTR"
batch_size = 256
trainer_params = {"max_epochs": 5,"enable_progress_bar": True,"accelerator": "auto","precision": "16-mixed","gradient_clip_val": None,"accumulate_grad_batches": 1,"devices": [0],
}
-
DEBUG = False
:設置調試標志,表示是否處于調試模式。 -
NORMALIZE = True
:設置規范化標志,表示是否對化學分子的SMILES表示進行規范化處理。 -
N_ROWS = 180_000_000
:設置處理的最大行數,這里指定了處理的數據集的行數上限。 -
assert N_ROWS is None or N_ROWS % 3 == 0
:使用assert
語句確保數據行數是3的倍數,因為后面的代碼根據這個假設進行數據處理。 -
if DEBUG:
:檢查是否處于調試模式。 -
N_SAMPLES = 10_000 if DEBUG else 2_000_000
:根據是否處于調試模式,設置樣本數目。 -
PROTEIN_NAMES = ["BRD4", "HSA", "sEH"]
:定義了需要處理的蛋白質名稱列表。 -
data_dir = Path("/kaggle/input/leash-BELKA")
:指定數據存儲的目錄路徑。 -
model_name = "DeepChem/ChemBERTa-10M-MTR"
:指定使用的預訓練BERT模型的名稱。 -
batch_size = 256
:指定訓練中使用的批量大小。 -
trainer_params
:定義了訓練器的參數,包括最大訓練周期數、是否啟用進度條、使用的加速器、精度、梯度裁剪值、累積梯度批次數以及使用的設備。
4、準備數據集
df = pl.read_parquet(Path(data_dir, "train.parquet"),columns=["molecule_smiles", "protein_name", "binds"],n_rows=N_ROWS,
)
test_df = pl.read_parquet(Path(data_dir, "test.parquet"),columns=["molecule_smiles"],n_rows=10000 if DEBUG else None,
)
df.head()
這里讀取了訓練數據和測試數據,并顯示了訓練數據的前幾行。
dfs = []
for i, protein_name in enumerate(PROTEIN_NAMES):sub_df = df[i::3]sub_df = sub_df.rename({"binds": protein_name})if i == 0:dfs.append(sub_df.drop(["id", "protein_name"]))else:dfs.append(sub_df[[protein_name]])
df = pl.concat(dfs, how="horizontal")
df = df.sample(n=N_SAMPLES)
print(df.head())
print(df[PROTEIN_NAMES].sum())
-
dfs = []
:初始化一個空列表,用于存儲每個蛋白質的子數據框。 -
for i, protein_name in enumerate(PROTEIN_NAMES):
:使用enumerate
函數遍歷PROTEIN_NAMES
列表中的每個蛋白質名稱,同時獲取其對應的索引值i
和名稱protein_name
。enumerate
函數用于同時遍歷一個可迭代對象(如列表、元組)中的元素及其對應的索引。 -
sub_df = df[i::3]
:根據索引i
對原始數據框df
進行切片操作,每隔3行取出一個子數據框。這樣可以將原始數據按照每個蛋白質分成3份,分別處理。 -
sub_df = sub_df.rename({"binds": protein_name})
:將子數據框的列名binds
重命名為當前蛋白質的名稱,以便后續處理和識別。 -
if i == 0:
:如果是第一個蛋白質,則將子數據框中的"id"和"protein_name"列刪除,并將處理后的子數據框添加到dfs
列表中。 -
else:
:如果不是第一個蛋白質,則只保留當前蛋白質的綁定情況數據,并將處理后的子數據框添加到dfs
列表中。 -
df = pl.concat(dfs, how="horizontal")
:將所有處理后的子數據框按水平方向拼接成一個新的數據框df
。 -
df = df.sample(n=N_SAMPLES)
:從新的數據框df
中隨機抽樣N_SAMPLES個樣本。 -
print(df.head())
:打印新數據框df
的前幾行。 -
print(df[PROTEIN_NAMES].sum())
:計算新數據框df
中每個蛋白質的綁定情況,并打印出來。
def normalize(x):mol = Chem.MolFromSmiles(x)smiles = Chem.MolToSmiles(mol, canonical=True, isomericSmiles=False)return smilesif NORMALIZE:df = df.with_columns(pl.col("molecule_smiles").map_elements(normalize, return_dtype=pl.Utf8))test_df = test_df.with_columns(pl.col("molecule_smiles").map_elements(normalize, return_dtype=pl.Utf8))
這部分代碼定義了一個函數normalize()
,用于對分子結構的SMILES表示進行規范化。如果NORMALIZE
為True,則對訓練數據和測試數據中的分子SMILES進行規范化處理。
-
normalize
函數:這個函數接受一個 SMILES 表示的分子作為輸入,并返回規范化后的 SMILES 表示。在這里,它使用了 RDKit 包中的功能。Chem.MolFromSmiles()
將輸入的 SMILES 字符串轉換為 RDKit 的分子對象,然后Chem.MolToSmiles()
將這個分子對象轉換回 SMILES 字符串,使用了canonical=True
參數確保生成的 SMILES 是規范化的,isomericSmiles=False
則確保不考慮分子的立體異構體。 -
if NORMALIZE:
:這個條件語句檢查一個名為NORMALIZE
的變量是否為真。如果為真,就會對數據進行規范化操作。如果NORMALIZE
是一個布爾值,這個條件通常會在前面被定義,以確定是否要進行規范化處理。 -
df = df.with_columns(pl.col("molecule_smiles").map_elements(normalize, return_dtype=pl.Utf8))
:這一行代碼應該是 Pandas 或類似的數據處理庫(比如 Dask 或 Modin)的語法。它將 DataFrame 中的"molecule_smiles"
列中的每個元素都應用normalize
函數進行規范化處理,然后將結果保存回原來的列中。這里使用了map_elements()
函數,它是 Pandas 的apply()
方法的替代,用于元素級別的操作。 -
test_df = test_df.with_columns(pl.col("molecule_smiles").map_elements(normalize, return_dtype=pl.Utf8))
:同上一行,只是這一行是對另一個 DataFrametest_df
執行同樣的操作。
train_idx, val_idx = train_test_split(np.arange(len(df)), test_size=0.2)
train_df, val_df = df[train_idx], df[val_idx]
len(train_df), len(val_df)
這段代碼將數據拆分為訓練集和驗證集,比例為80:20。
?
-
train_test_split(np.arange(len(df)), test_size=0.2)
:這行代碼使用了train_test_split
函數來將索引數組np.arange(len(df))
分割成訓練集和驗證集的索引。參數test_size=0.2
指定了驗證集所占的比例,這里是20%。訓練集和驗證集的索引分別存儲在train_idx
和val_idx
中。 -
train_df, val_df = df[train_idx], df[val_idx]
:這行代碼使用了 Pandas 或類似的庫的索引功能,將原始 DataFramedf
中對應索引的行劃分給訓練集和驗證集,分別存儲在train_df
和val_df
中。 -
len(train_df), len(val_df)
:這一行簡單地打印出了訓練集和驗證集的長度,以確認它們的大小是否符合預期。
5、建立數據集
tokenizer = AutoTokenizer.from_pretrained(model_name)
這行代碼根據給定的模型名稱加載了相應的tokenizer,用于將文本數據轉換成模型輸入。
def tokenize(batch, tokenizer):output = tokenizer(batch["molecule_smiles"], truncation=True)return outputclass LMDataset(Dataset):def __init__(self, df, tokenizer, stage="train"):assert stage in ["train", "val", "test"]self.tokenizer = tokenizerself.stage = stagedf = (datasets.Dataset.from_pandas(df.to_pandas()).map(tokenize, batched=True, fn_kwargs={"tokenizer": self.tokenizer}).to_pandas())self.df = pl.from_pandas(df)def __len__(self):return len(self.df)def __getitem__(self, index):data = self._generate_data(index)data["label"] = self._generate_label(index)return data def _generate_data(self, index):data = {"input_ids": np.array(self.df[index, "input_ids"]),"attention_mask": np.array(self.df[index, "attention_mask"]),}return datadef _generate_label(self, index):if self.stage == "test":return np.array([0, 0, 0])else:return self.df[index, PROTEIN_NAMES].to_numpy()[0]LMDataset(train_df[:100], tokenizer)[0]
-
tokenize
函數:這是一個輔助函數,接受一個批次的數據和一個分詞器(tokenizer),并對批次中的分子 SMILES 進行分詞處理。在這里,使用了 Hugging Face Transformers 庫提供的tokenizer
對批次中的分子 SMILES 進行分詞,設置了truncation=True
以確保分詞后的序列長度不超過指定的最大長度。 -
LMDataset
類:這是一個自定義的 PyTorch Dataset 類,用于加載數據集。主要功能包括:__init__
方法:初始化數據集對象。接受一個 DataFramedf
、一個分詞器tokenizer
和一個階段stage
(默認為 "train")。在這個方法中,將輸入的 DataFrame 轉換為 PyTorch 的 Dataset 對象,并使用map
方法對數據進行分詞處理,得到包含分詞結果的 DataFrame,并將其轉換為 Polars 的 DataFrame。__len__
方法:返回數據集的長度,即數據集中樣本的數量。__getitem__
方法:根據給定的索引index
返回對應的樣本數據和標簽。在這里,調用了_generate_data
和_generate_label
方法來生成數據和標簽。_generate_data
方法:根據給定的索引index
生成數據。返回一個字典,包含 "input_ids" 和 "attention_mask"。_generate_label
方法:根據給定的索引index
生成標簽。如果階段為 "test",則返回一個全為 0 的數組;否則,根據索引獲取對應的樣本的標簽值。
-
LMDataset(train_df[:100], tokenizer)[0]
:創建了一個LMDataset
對象,并取出第一個樣本的數據和標簽。
class LBDataModule(L.LightningDataModule):def __init__(self, train_df, val_df, test_df, tokenizer):super().__init__()self.train_df = train_dfself.val_df = val_dfself.test_df = test_dfself.tokenizer = tokenizerdef _generate_dataset(self, stage):if stage == "train":df = self.train_dfelif stage == "val":df = self.val_dfelif stage == "test":df = self.test_dfelse:raise NotImplementedErrordataset = LMDataset(df, self.tokenizer, stage=stage)return datasetdef _generate_dataloader(self, stage):dataset = self._generate_dataset(stage)if stage == "train":shuffle=Truedrop_last=Trueelse:shuffle=Falsedrop_last=Falsereturn DataLoader(dataset,batch_size=batch_size,shuffle=shuffle,drop_last=drop_last,pin_memory=True,collate_fn=DataCollatorWithPadding(self.tokenizer),)def train_dataloader(self):return self._generate_dataloader("train")def val_dataloader(self):return self._generate_dataloader("val")def test_dataloader(self):return self._generate_dataloader("test")datamodule = LBDataModule(train_df, val_df, test_df, tokenizer)
-
__init__
方法:初始化 DataModule 類,接受訓練集、驗證集、測試集的 DataFrame 數據以及一個分詞器 tokenizer。在這個方法中,將輸入的數據和分詞器保存到類的屬性中。 -
_generate_dataset
方法:根據給定的階段("train"、"val" 或 "test"),生成對應階段的數據集對象。根據階段選擇對應的 DataFrame 數據,然后使用LMDataset
類創建相應的數據集對象,并傳入對應的 DataFrame 和分詞器。 -
_generate_dataloader
方法:根據給定的階段("train"、"val" 或 "test"),生成對應階段的數據加載器對象。調用_generate_dataset
方法生成對應階段的數據集對象,然后根據階段設定加載器的參數,如是否打亂數據、是否丟棄最后一個不完整的批次等,最后使用 PyTorch 的 DataLoader 類創建數據加載器對象。 -
train_dataloader
、val_dataloader
和test_dataloader
方法:分別返回訓練、驗證和測試階段的數據加載器對象,通過調用_generate_dataloader
方法實現。 -
datamodule
實例:使用給定的訓練集、驗證集、測試集和分詞器創建了一個 DataModule 對象。
6、建立模型
class LMModel(nn.Module):def __init__(self, model_name):super().__init__()self.config = AutoConfig.from_pretrained(model_name, num_labels=3)self.lm = AutoModel.from_pretrained(model_name, add_pooling_layer=False)self.dropout = nn.Dropout(self.config.hidden_dropout_prob)self.classifier = nn.Linear(self.config.hidden_size, self.config.num_labels)self.loss_fn = nn.BCEWithLogitsLoss(reduction="mean")def forward(self, batch):last_hidden_state = self.lm(batch["input_ids"],attention_mask=batch["attention_mask"],).last_hidden_statelogits = self.classifier(self.dropout(last_hidden_state[:, 0]))return {"logits": logits,}def calculate_loss(self, batch):output = self.forward(batch)loss = self.loss_fn(output["logits"], batch["labels"].float())output["loss"] = lossreturn outputLMModel(model_name)
-
class LMModel(nn.Module):
: 這是定義了一個名為LMModel
的類,它繼承自nn.Module
,表示這個類是一個 PyTorch 模型。 -
def __init__(self, model_name):
: 這是LMModel
類的初始化函數,接受一個參數model_name
,表示要使用的預訓練語言模型的名稱。 -
self.config = AutoConfig.from_pretrained(model_name, num_labels=3)
: 使用 Hugging Face 的AutoConfig
類從預訓練模型model_name
中加載配置。num_labels=3
指定了模型的輸出類別數量為 3。 -
self.lm = AutoModel.from_pretrained(model_name, add_pooling_layer=False)
: 使用 Hugging Face 的AutoModel
類從預訓練模型model_name
中加載模型,add_pooling_layer=False
表示不添加池化層。 -
self.dropout = nn.Dropout(self.config.hidden_dropout_prob)
: 創建了一個 Dropout 層,用于在訓練過程中隨機將一部分神經元的輸出置為零,以防止過擬合。 -
self.classifier = nn.Linear(self.config.hidden_size, self.config.num_labels)
: 創建了一個線性層,將語言模型的隱藏狀態映射到指定數量的標簽上。 -
self.loss_fn = nn.BCEWithLogitsLoss(reduction="mean")
: 創建了一個二元交叉熵損失函數,用于計算模型的損失。這里使用BCEWithLogitsLoss
是因為多標簽分類問題中每個標簽都是獨立的,所以對每個標簽使用二元交叉熵損失。 -
def forward(self, batch):
: 這是模型的前向傳播函數,接受一個批次的輸入數據batch
,包括輸入的標記化的文本和注意力掩碼。 -
last_hidden_state = self.lm(
: 使用加載的語言模型處理輸入數據,得到最后一層的隱藏狀態。 -
logits = self.classifier(
: 將最后一層隱藏狀態經過線性層映射到標簽空間,得到每個標簽的預測概率。 -
def calculate_loss(self, batch):
: 這是計算模型損失的函數,接受一個批次的輸入數據batch
。 -
output = self.forward(batch)
: 調用前向傳播函數得到模型的輸出結果。 -
loss = self.loss_fn(output["logits"], batch["labels"].float())
: 使用損失函數計算模型的損失,將預測的概率和真實標簽進行比較。 -
output["loss"] = loss
: 將計算得到的損失保存在輸出字典中。
class LBModelModule(L.LightningModule):def __init__(self, model_name):super().__init__()self.model = LMModel(model_name)self.map = AveragePrecision(task="binary")def forward(self, batch):return self.model(batch)def calculate_loss(self, batch, batch_idx):return self.model.calculate_loss(batch)def training_step(self, batch, batch_idx):ret = self.calculate_loss(batch, batch_idx)self.log("train_loss", ret["loss"], on_step=True, on_epoch=True, prog_bar=True, sync_dist=True)return ret["loss"]def validation_step(self, batch, batch_idx):ret = self.calculate_loss(batch, batch_idx)self.log("val_loss", ret["loss"], on_step=False, on_epoch=True, prog_bar=True, sync_dist=True)self.map.update(F.sigmoid(ret["logits"]), batch["labels"].long())def on_validation_epoch_end(self):val_map = self.map.compute()self.log("val_map", val_map, on_step=False, on_epoch=True, prog_bar=True, sync_dist=True)self.map.reset()def predict_step(self, batch, batch_idx, dataloader_idx=0):logits = self.forward(batch)["logits"]probs = F.sigmoid(logits)return probsdef configure_optimizers(self):optimizer = torch.optim.Adam(self.parameters(), lr=0.0001)return {"optimizer": optimizer,}modelmodule = LBModelModule(model_name)
-
class LBModelModule(L.LightningModule):
: 這是定義了一個名為LBModelModule
的類,它繼承自 PyTorch Lightning 的LightningModule
類,表示這個類是一個 Lightning 模塊。 -
def __init__(self, model_name):
: 這是LBModelModule
類的初始化函數,接受一個參數model_name
,表示要使用的語言模型的名稱。 -
self.model = LMModel(model_name)
: 創建了一個LMModel
類的實例作為模型的成員變量,用于處理輸入數據并進行預測。 -
self.map = AveragePrecision(task="binary")
: 創建了一個用于計算平均精度的AveragePrecision
類的實例。 -
def forward(self, batch):
: 這是模型的前向傳播函數,接受一個批次的輸入數據batch
,并調用模型的前向傳播函數。 -
def calculate_loss(self, batch, batch_idx):
: 這是計算損失的函數,接受一個批次的輸入數據batch
和批次的索引batch_idx
。 -
def training_step(self, batch, batch_idx):
: 這是訓練步驟函數,在每個訓練步驟中計算損失并更新訓練日志。 -
def validation_step(self, batch, batch_idx):
: 這是驗證步驟函數,在每個驗證步驟中計算損失并更新驗證日志。 -
def on_validation_epoch_end(self):
: 這是在每個驗證輪結束后調用的函數,在這里計算驗證集上的平均精度并重置計算器。 -
def predict_step(self, batch, batch_idx, dataloader_idx=0):
: 這是預測步驟函數,用于在每個預測步驟中生成預測概率。 -
def configure_optimizers(self):
: 這是配置優化器的函數,返回一個優化器的配置字典,這里使用 Adam 優化器。
7、訓練
checkpoint_callback = ModelCheckpoint(filename=f"model-{{val_map:.4f}}",save_weights_only=True,monitor="val_map",mode="max",dirpath="/kaggle/working",save_top_k=1,verbose=1,
)
early_stop_callback = EarlyStopping(monitor="val_map", mode="max", patience=3)
progress_bar_callback = TQDMProgressBar(refresh_rate=1)
callbacks = [checkpoint_callback,early_stop_callback,progress_bar_callback,
]
-
ModelCheckpoint
: 這個回調函數用于在訓練過程中保存模型的檢查點,以便在訓練結束后選擇最佳的模型參數。參數解釋如下:filename
: 模型文件名的格式,可以使用格式化字符串指定變量,這里使用了驗證集的平均精度作為文件名。save_weights_only
: 設置為True
表示只保存模型的權重。monitor
: 監控的指標,這里選擇了驗證集的平均精度。mode
: 模型選擇的模式,這里選擇了最大化驗證集的平均精度。dirpath
: 模型保存的目錄路徑。save_top_k
: 保存最佳模型的數量,這里設置為 1。verbose
: 控制是否在保存模型時輸出信息,設置為 1 表示輸出信息。
-
EarlyStopping
: 這個回調函數用于在驗證集指標停止提升時提前停止訓練,以防止過擬合。參數解釋如下:monitor
: 監控的指標,這里同樣選擇了驗證集的平均精度。mode
: 模型選擇的模式,這里同樣選擇了最大化驗證集的平均精度。patience
: 容忍多少個 epoch 內指標沒有提升。
-
TQDMProgressBar
: 這個回調函數用于在訓練過程中顯示進度條,以便實時監控訓練進度。參數refresh_rate
控制刷新頻率,即進度條更新的時間間隔。
trainer = L.Trainer(callbacks=callbacks, **trainer_params)
trainer.fit(modelmodule, datamodule)
-
trainer = L.Trainer(callbacks=callbacks, **trainer_params)
: 這行代碼創建了一個 Lightning Trainer 對象,并設置了回調函數和訓練參數。參數解釋如下:callbacks
: 指定了訓練過程中要使用的回調函數列表,這里使用了之前定義的callbacks
列表,包括模型檢查點、提前停止和進度條回調。**trainer_params
: 使用**
語法將字典trainer_params
中的所有鍵值對作為參數傳遞給Trainer
對象,這里包括了一系列訓練參數,如最大 epoch 數、設備選擇、精度設置等。
-
trainer.fit(modelmodule, datamodule)
: 這行代碼使用創建的 Trainer 對象開始了模型的訓練過程。參數解釋如下:modelmodule
: 指定了要訓練的 Lightning 模型,這里是之前定義的LBModelModule
對象。datamodule
: 指定了訓練數據的 datamodule,這里是之前定義的LBDataModule
對象,其中包含了訓練、驗證和測試數據集的處理邏輯。
8、推理
test_df = pl.read_parquet(Path(data_dir, "test.parquet"),columns=["molecule_smiles"],n_rows=10000 if DEBUG else None,
)
pl.read_parquet
: 這是 PyTorch Lightning 庫中的一個函數,用于從 Parquet 格式的文件中讀取數據。Parquet 是一種列式存儲格式,常用于大規模數據存儲和處理。Path(data_dir, "test.parquet")
: 這是指定要讀取的 Parquet 文件的路徑。data_dir
是之前定義的數據目錄路徑,"test.parquet"
是要讀取的文件名。columns=["molecule_smiles"]
: 這是指定要讀取的列名,即從 Parquet 文件中選擇的列。在這里,只選擇了名為 "molecule_smiles" 的列。n_rows=10000 if DEBUG else None
: 這是指定要讀取的行數。如果DEBUG
變量為True
,則只讀取文件的前 10000 行;否則,讀取整個文件。這是一種常用的技術,用于在開發和調試階段快速處理較小的數據樣本,以加快開發速度。
working_dir = Path("/kaggle/working")
model_paths = working_dir.glob("*.ckpt")
test_dataloader = datamodule.test_dataloader()
for model_path in model_paths:print(model_path)modelmodule = LBModelModule.load_from_checkpoint(checkpoint_path=model_path,model_name=model_name,)predictions = trainer.predict(modelmodule, test_dataloader)predictions = torch.cat(predictions).numpy()pred_dfs = []for i, protein_name in enumerate(PROTEIN_NAMES):pred_dfs.append(test_df.with_columns(pl.lit(protein_name).alias("protein_name"),pl.lit(predictions[:, i]).alias("binds"),))pred_df = pl.concat(pred_dfs)submit_df = (pl.read_parquet(Path(data_dir, "test.parquet"), columns=["id", "molecule_smiles", "protein_name"]).join(pred_df, on=["molecule_smiles", "protein_name"], how="left").select(["id", "binds"]).sort("id"))submit_df.write_csv(Path(working_dir, f"submission_{model_path.stem}.csv"))
- 定義了一個工作目錄
working_dir
,這是保存模型和生成提交文件的目錄。 - 使用
glob
方法獲取工作目錄中的所有模型文件的路徑,并存儲在model_paths
變量中。 - 獲取測試數據的數據加載器
test_dataloader
。 - 遍歷模型文件路徑列表
model_paths
。 - 加載每個模型并進行預測。首先,使用
LBModelModule.load_from_checkpoint
方法加載模型,并傳入模型文件路徑和模型名稱。然后使用trainer.predict
方法對測試數據進行預測,得到預測結果。 - 將預測結果與相應的蛋白質名稱關聯,并將結果存儲在
pred_df
中。 - 讀取測試數據的 Parquet 文件,并選擇需要的列("id"、"molecule_smiles" 和 "protein_name")。
- 將預測結果與測試數據合并,并只保留 "id" 和 "binds" 列。
- 將合并后的結果按照 "id" 進行排序。
- 將生成的提交文件寫入 CSV 格式,并根據當前模型文件的名稱進行命名,存儲在工作目錄中。
9、集成
sub_files = list(working_dir.glob("submission_*.csv"))
sub_files
?sub_files
是一個列表,包含工作目錄中所有以 "submission_" 開頭且以 ".csv" 結尾的文件的路徑。這些文件名可能會因為模型路徑的不同而有所變化,但它們都是模型預測結果的提交文件。
sub_dfs = []
for sub_file in sub_files:sub_dfs.append(pl.read_csv(sub_file))
submit_df = (pl.concat(sub_dfs).group_by("id").agg(pl.col("binds").mean()).sort("id")
)
- 循環遍歷
sub_files
中的每個文件路徑。 - 對于每個文件,使用
pl.read_csv(sub_file)
讀取 CSV 文件并將其添加到sub_dfs
列表中。 - 使用
pl.concat(sub_dfs)
將所有 DataFrame 連接成一個大的 DataFrame。 - 使用
group_by("id")
將數據按照 "id" 列進行分組。 - 使用
.agg(pl.col("binds").mean())
對每個分組計算 "binds" 列的平均值。 - 最后使用
.sort("id")
按照 "id" 列進行排序。
!rm -fr *
?這是一個Unix/Linux命令,用于刪除當前目錄下的所有文件和文件夾。具體地說:
rm
是 remove 的縮寫,用于刪除文件或目錄。-fr
是兩個選項的結合:-f
表示強制刪除,即不會提示確認。-r
表示遞歸刪除,即刪除目錄及其下所有文件和子目錄。
*
是通配符,代表當前目錄下的所有文件和文件夾。
因此,這個命令的意思是:刪除當前目錄下的所有文件和文件夾,且不會提示確認。
submit_df.write_csv(Path(working_dir, "submission.csv"))
submit_df
是一個 DataFrame,其中包含了提交的數據。write_csv
是一個方法,用于將 DataFrame 寫入到 CSV 格式的文件中。Path(working_dir, "submission.csv")
創建了一個路徑對象,指定了要寫入的文件路徑,這個路徑對象表示當前工作目錄下的 "submission.csv" 文件。
?10、改進方向
- 尋找最佳的交叉驗證策略
- 增加數據
- 使用更多的特征
- 更大的模型
- 調整超參數
- 集成模型
源代碼:Leash Bio: ChemBERTa with all data (kaggle.com)