从零基础开始对机器学习的初步了解
文章目录
前言
1.机器学习(machine learning)
咱们可以把机器学习想象成一种可以让计算机学会自己做决定的方法,而不是需要我们明确的告诉它每一步该怎么做。
想象你要教一个小孩子识别🍎和🍊。首先,你需要准备大量苹果和橙子的照片。同时呢,要确保每张照片都清晰可见,易于分辨,这样更方便学习。接着,我们提取二者的不同点:🍎通常是红色的,表面光滑;而🍊是橙色的,表面有点凹凸不平。这里的“颜色”和“表面质地”就是我们所说的“特征”。通过这些特征,孩子就可以更好地分辨出苹果和橙子。然后,我们要决定用什么方法来教会孩子分辨苹果和橙子。比如,选择了监督学习,你就会一边给孩子看图片一边告诉他这是苹果还是橙子。慢慢地,孩子就学会了如何区分它们了;而选择无监督学习,你就会让孩子自己去观察,找出二者的不同进而分辨出来……为了检查孩子是否真的学会了,你可以给他看一些之前没见过的新图片,看看他能否正确地分辨出苹果和橙子。当孩子学会后,你就可以让他帮你挑选水果了,问他这是苹果还是橙子。如果孩子有时仍犯错,你可以继续教他,直到他能准确地区分🍎和🍊为止。
机器学习的工作原理类似。我们会喂给计算机大量的数据(比如🍎和🍊的照片),并且告诉计算机哪些是🍎,哪些是🍊。然后,计算机会通过使用特定的算法,慢慢尝试找出二者的区别。经过一段时间的学习后,当计算机看到一张新的照片时,它就能自己做出判断了。
机器学习的基本思想:让计算机通过学习数据的规律,然后用学到的知识去做一些有用的判断或决策。
2.问题分类
机器学习问题常见的分类角度有学习范式
、问题类型
、数据特性
等,今天呢我们从最常见的分类角度:学习范式
(就像是咱们在学校时采用了不同学习方法。同样机器学习中,学习范式定义了计算机是如何从数据中学习的)去了解一下:
(1)监督学习(Supervised Learning)
想象一下你正在参加一个派对,派对上的人拿着贴有不同标签的饮料(有标签的训练数据)
:比如可乐,雪碧、橙汁等等。现在你有一个任务:学会如何区分这些饮料。
①分类问题
分类问题是指我们希望预测一个离散的类别
:你的面前有一排饮料,你想要学习如何识别不同的饮料。每一种饮料都有明确的标签,告诉你这杯是可乐,那杯是橙汁,另一杯是雪碧。你希望通过观察这些饮料的特点(比如颜色、包装等)来学习如何区分它们。当你遇到一个新的饮料时,你就可以根据学到的知识来判断它是什么。
②回归问题
回归问题是指我们希望预测一个连续的数值
:这次你想要估计某杯饮料的温度。此时标签上写的是每种饮料的温度(例如:10°C、15°C、20°C)。你的任务是通过观察饮料的某些特征(如是否有冷凝水珠、是否冒气泡等)来学习如何估计它的温度。当你看到一个新的饮料时,你可以尝试根据之前学到的规律来预测它的温度。
本文学习解析的赛题就是一个典型的回归问题
(2)无监督学习(Unsupervised Learning)
中途你被带到了一个完全陌生的派对,这里的人们拿着各种各样的饮料,但是没有任何标签告诉你这是什么,有没有任何人教你。
①酯类问题
在这种情况下,你尝试自己观察,你发现有的饮料颜色相似,有的饮料闻的味道相似。于是,你就把这些有相似之处的饮料放在一起(数据的自然分组)
,堆成了几个小堆。这就是酯类问题,根据共同特征分组。
①降维问题
你又尝试用最简单的方式来描述这些饮料的不同。过程中发现:有些放在一起的饮料你可以仅通过气味就区分他们(简化数据表示)
。这就是降维问题,用更少的信息描述数据的主要特征。
(3)半监督学习(Semi-Supervised learning)
现在,你又回到了那个派对,但是这次只有一部分饮料(少量)
贴有标签,其余的(大量)
什么都没有。你需要利用那些有标签的来帮助你判断那些没有标签的饮料是什么。这就是半监督学习,利用少量已知的信息来辅助学习未知的信息。
(4)强化学习(Reinforcement Learning)
最后,你被派去打乒乓球,虽然你不怎么会打,但是你有一个极好的教练。每次你打球时,教练都会及时得反馈你打得好还是不好。随着时间的推移,你通过试错学会了如何更好地打球。这就是强化学习,通过与环境的交互
来学习如何做出决策,以便最大化收益。
提示:以下是本篇文章正文内容
一、问题描述
背景知识
碳氮成键反应、Diels-Alder环加成反应等一系列催化合成反应,被广泛应用于各类药物的生产合成中。研究人员与产业界在针对特定反应类型开发新的催化合成方法时,往往追求以高产率获得目标产物,也即开发高活性的催化反应体系,以提升原子经济性,减少资源的浪费与环境污染。然而,开发具有高活性的催化反应体系通常需要对包括催化剂和溶剂在内的多种反应条件进行详尽的探索,这导致了它成为了一项极为耗时且资源密集的任务。这要求对包括催化剂和溶剂在内的多种反应条件进行详尽的探索。目前,反应条件的筛选在很大程度上依赖于经验判断和偶然发现,导致催化反应条件的优化过程既耗时又费力,并且严重制约了新的高效催化合成策略的开发。
反应底物和反应条件是决定其产率的关键因素。因此,我们可以利用AI模型来捕捉底物、条件与产率之间的内在联系。借助产率预测AI模型,仅需输入底物和条件的信息,我们就能够预测该反应组合下的产率,从而有效提升催化反应的条件筛选效率。
本次比赛提供在药物合成中常见的多种催化反应实验数据,其中包括反应的底物、包括催化剂在内的反应添加剂、反应溶剂以及反应产物,期待选手通过分析反应数据,利用机器学习、深度学习算法或者大语言模型,建立产率预测模型,从而辅助未知新反应的反应条件筛选。
任务
构建一个能够准确预测碳氮成键反应产率的预测模型
通过对反应中所包含的反应底物、添加剂、溶剂以及产物进行合理的特征化,运用机器学习模型或者深度学习模型拟合预测反应的产率。或者利用训练集数据对开源大语言模型进行微调以预测反应的产率
二、baseline学习
1.Import dependency
pip
命令安装库
!pip install pandas #用于数据处理和分析
!pip install -U scikit-learn#用于机器学习(-U 参数来确保安装的是最新版本)
!pip install rdkit #用于化学信息学和分子操作
- 导入库
# 导入库
import pickle #用于序列化和反序列化Python对象
import pandas as pd #用于数据处理和分析
from tqdm import tqdm #用于显示进度条
from sklearn.ensemble import RandomForestRegressor#用于回归任务的随机森林模型
from rdkit.Chem import rdMolDescriptors #用于计算分子描述符
from rdkit import RDLogger,Chem #用于处理分子和管理日志
import numpy as np #用于数值计算
RDLogger.DisableLog('rdApp.*') #禁用了RDKit的日志输出,以减少不必要的日志信息,保持输出的简洁
- 生成分子的
Morgan 指纹
(fingerprint)一种常用于化学信息学的分子描述符,能够表示分子的结构特征
def mfgen(mol,nBits=2048, radius=2):
'''
Parameters
----------
mol : mol
RDKit的分子对象(mol),表示要生成指纹的分子
nBits : int
指纹的位数,默认为2048.这个参数决定了生成的指纹的长度
radius : int
Morgan指纹的半径,默认为2.这个参数决定了指纹生成时考虑的原子邻域的大小
Returns
-------
mf_desc_map : ndarray
一个包含分子指纹描述符的NumPy数组
'''
# 生成分子的Morgan指纹(指纹是一个位向量,其中每一位表示分子结构的某个特征是否存在)
fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol,radius=radius,nBits=nBits)
# 将位向量转换为一个字符串,其中每个字符是 '0' 或 '1',表示相应位的值.然后转换为NumPy数组
return np.array(list(map(eval,list(fp.ToBitString()))))
'''
fp.ToBitString() 生成一个位字符串
list(fp.ToBitString()) 将位字符串转换为字符列表
map(eval, list(fp.ToBitString())) 将每个字符('0' 或 '1')转换为整数(0 或 1)
list(map(eval, list(fp.ToBitString()))) 将映射结果转换为列表
np.array(...) 将列表转换为NumPy数组
'''
- 将一组SMILES字符串(表示化学分子)转换为其对应的分子指纹向量
# 加载数据
def vec_cpd_lst(smi_lst):
'''
参数
----------
smi_lst: list of str
一个包含SMILES字符串的列表,每个字符串表示一个化学分子
返回
-------
vec_lst: np.ndarray
一个NumPy数组,其中每一行是一个分子的指纹向量
'''
# 将输入的SMILES字符串列表转换为集合,以去除重复的SMILES字符串,然后再将集合转换回列表
smi_set = list(set(smi_lst))
# 初始化一个空字典 smi_vec_map,用于存储每个SMILES字符串及其对应的分子指纹向量
smi_vec_map = {}
# 遍历去重后的SMILES字符串列表 smi_set,并为每个SMILES字符串生成其对应的分子指纹向量
for smi in tqdm(smi_set): # tqdm:显示进度条
mol = Chem.MolFromSmiles(smi) # 将SMILES字符串转换为RDKit的分子对象
if mol is not None:
smi_vec_map[smi] = mfgen(mol) # 生成分子的Morgan指纹向量并存储在字典smi_vec_map中,以SMILES字符串为键
else:
smi_vec_map[smi] = np.zeros(2048) # 如果无法生成分子对象,分配一个全零的指纹向量
# 为空字符串 '' 分配一个全零的指纹向量,长度为2048位
smi_vec_map[''] = np.zeros(2048)
# 根据原始的SMILES字符串列表 smi_lst 生成对应的指纹向量列表 vec_lst
# 通过查找字典 smi_vec_map,将每个SMILES字符串映射到其指纹向量
vec_lst = [smi_vec_map[smi] for smi in smi_lst]
return np.array(vec_lst)
2.Vectorization
- 加载数据
dataset_dir = '../dataset' #定义了数据集所在的目录路径
# 注:如果是在AI Studio上,将这里改为'dataset'
# 加载csv文件
train_df = pd.read_csv(f'{dataset_dir}/round1_train_data.csv')
test_df = pd.read_csv(f'{dataset_dir}/round1_test_data.csv')
# 打印数据集大小
print(f'Training set size: {len(train_df)}, test set size: {len(test_df)}')
- 获取特征向量
# 从csv中读取数据,提取SMILES字符串转换为列表
train_rct1_smi = train_df['Reactant1'].to_list()
train_rct2_smi = train_df['Reactant2'].to_list()
train_add_smi = train_df['Additive'].to_list()
train_sol_smi = train_df['Solvent'].to_list()
# 将SMILES转化为分子指纹向量
train_rct1_fp = vec_cpd_lst(train_rct1_smi)
train_rct2_fp = vec_cpd_lst(train_rct2_smi)
train_add_fp = vec_cpd_lst(train_add_smi)
train_sol_fp = vec_cpd_lst(train_sol_smi)
# 拼接分子指纹向量
train_x=np.concatenate([train_rct1_fp,train_rct2_fp,train_add_fp,train_sol_fp],axis=1)#将反应物1、反应物2、添加剂和溶剂的分子指纹向量在第1维度(列)上进行拼接,形成一个完整的特征向量 train_x
# 提取目标变量
train_y = train_df['Yield'].to_numpy()#从训练数据框中提取出目标变量(产率),并将其转换为NumPy数组 train_y
# 测试集也进行同样的操作
test_rct1_smi = test_df['Reactant1'].to_list()
test_rct2_smi = test_df['Reactant2'].to_list()
test_add_smi = test_df['Additive'].to_list()
test_sol_smi = test_df['Solvent'].to_list()
test_rct1_fp = vec_cpd_lst(test_rct1_smi)
test_rct2_fp = vec_cpd_lst(test_rct2_smi)
test_add_fp = vec_cpd_lst(test_add_smi)
test_sol_fp = vec_cpd_lst(test_sol_smi)
# 提取SMILES字符串,转换为分子指纹,拼接成特征向量
test_x = np.concatenate([test_rct1_fp,test_rct2_fp,test_add_fp,test_sol_fp],axis=1)
'''
train_x 和 test_x 可以作为机器学习模型的输入特征
train_y 则是模型的目标变量
'''
3.Model fitting and saving
- 训练模型
# 实例化模型,并指定重要参数
model = RandomForestRegressor(n_estimators=10,max_depth=10,min_samples_split=2,min_samples_leaf=1,n_jobs=-1)
# n_estimators=10:森林中树的数量
# max_depth=10:树的最大深度
# min_samples_split=2:内部节点再划分所需的最小样本数
# min_samples_leaf=1:叶子节点所需的最小样本数
# n_jobs=-1:使用所有可用的处理器进行并行计算
# 基于训练数据 train_x 和 train_y训练模型
model.fit(train_x,train_y)
- 保存模型
with open('./random_forest_model.pkl', 'wb') as file:
pickle.dump(model, file)
- 加载模型
with open('random_forest_model.pkl', 'rb') as file:
loaded_model = pickle.load(file)
- 预测&推理
test_pred = loaded_model.predict(test_x)
4.Answer generation
- 保存文件
ans_str_lst = ['rxnid,Yield']# 初始化列表,添加表头
for idx,y in enumerate(test_pred):
ans_str_lst.append(f'test{idx+1},{y:.4f}')# 添加数据
with open('./submit.txt','w') as fw:
fw.writelines('\n'.join(ans_str_lst))#写入文件
让我们看看战果如何