#Datawhale AI夏令营
task2的任务是精读baseline
class GenomicTokenizer:
def __init__(self, ngram=5, stride=2):
# 初始化分词器,设置n-gram长度和步幅
self.ngram = ngram
self.stride = stride
def tokenize(self, t):
# 将输入序列转换为大写
t = t.upper()
if self.ngram == 1:
# 如果n-gram长度为1,直接将序列转换为字符列表
toks = list(t)
else:
# 否则,按照步幅对序列进行n-gram分词
toks = [t[i:i+self.ngram] for i in range(0, len(t), self.stride) if len(t[i:i+self.ngram]) == self.ngram]
# 如果最后一个分词长度小于n-gram,移除最后一个分词
if len(toks[-1]) < self.ngram:
toks = toks[:-1]
# 返回分词结果
return toks
这一部分是在将基因组序列分割成固定长度的n-gram:
__init__方法接受两个参数ngram和stride,用于设置分词器的n-gram长度和步幅。当ngram设置为1时,它确实将输入序列转换为单个字符的列表;当ngram大于1时,它通过步幅来生成ngram分词,但当步幅小于ngram长度,这将导致在某些情况下无法正确生成ngram分词。此外,即使步幅足够大,也可能因为输入序列的长度不是步幅的整数倍而导致最后一个分词长度小于ngram。
class SiRNADataset(Dataset):
def __init__(self, df, columns, vocab, tokenizer, max_len, is_test=False):
# 初始化数据集
self.df = df # 数据框
self.columns = columns # 包含序列的列名
self.vocab = vocab # 词汇表
self.tokenizer = tokenizer # 分词器
self.max_len = max_len # 最大序列长度
self.is_test = is_test # 指示是否是测速集
def __len__(self):
# 返回数据集的长度
return len(self.df)
def __getitem__(self, idx):
# 获取数据集中的第idx个样本
row = self.df.iloc[idx] # 获取第idx行数据
# 对每一列进行分词和编码
seqs = [self.tokenize_and_encode(row[col]) for col in self.columns]
if self.is_test:
# 仅返回编码后的序列(非测试集模式)
return seqs
else:
# 获取目标值并转换为张量(仅在非测试集模式下)
target = torch.tensor(row['mRNA_remaining_pct'], dtype=torch.float)
# 返回编码后的序列和目标值
return seqs, target
def tokenize_and_encode(self, seq):
if ' ' in seq: # 修改过的序列
tokens = seq.split() # 按空格分词
else: # 常规序列
tokens = self.tokenizer.tokenize(seq) # 使用分词器分词
# 将token转换为索引,未知token使用0(<pad>)
encoded = [self.vocab.stoi.get(token, 0) for token in tokens]
# 将序列填充到最大长度
padded = encoded + [0] * (self.max_len - len(encoded))
# 返回张量格式的序列
return torch.tensor(padded[:self.max_len], dtype=torch.long)
这一部分用于加载siRNA数据,并将序列数据转换为模型可以处理的格式:
类定义和初始化部分已经清晰,包括了处理数据集所需的所有必要信息,但需要注意的是,is_test参数用于区分测试集和非测试集,所以确保这个参数在使用时保持一致是很重要的。tokenize_and_encode方法检查序列中是否包含空格,如果是,则按空格分词;否则,使用自定义分词器分词。这种方法非常灵活,能让我了解如何处理不同类型的输入序列。序列被填充到最大长度self.max_len,这是处理变长序列时的常见做法。但是,baseline在这里使用了[0]*(self.max_len-len(encoded))来生成填充,然后又使用 padded[:self.max_len],这实际上是多余的,因为padded的长度已经是self.max_len,可以简化为padded=encoded+[0]*(self.max_len-len(encoded))[:self.max_len].
train_data = lgb.Dataset(X_train, label=y_train)
test_data = lgb.Dataset(X_test, label=y_test, reference=train_data)
def print_validation_result(env):
result = env.evaluation_result_list[-1]
print(f"[{env.iteration}] {result[1]}'s {result[0]}: {result[2]}")
params = {
"boosting_type": "gbdt",
"objective": "regression",
"metric": "root_mean_squared_error",
"max_depth": 7,
"learning_rate": 0.02,
"verbose": 0,
}
gbm = lgb.train(
params,
train_data,
num_boost_round=15000,
valid_sets=[test_data],
callbacks=[print_validation_result],
)
这是有大佬使用了lightgbm模型对官方baseline做出的修改,并且跑到了0.75的分数。注意在使用这个修改的模型前,需要在控制台上pip install lightgbm,否则会报错。至于如何读懂修改后的模型,我仍在不断学习中。