AI / DL/생명과학을 위한 딥러닝

[생명과학을 위한 딥러닝] 4장. 분자 수준 데이터 다루기

bri9htstar 2022. 10. 18. 15:09

분자 수준의 데이터에 머신러닝을 사용하는 것은 재료화학과 화학 분야에서 특히 유용하다. 노동 집약적인 실험의 양을 줄이고 머신러닝을 통해 새로운 분자 구조를 예측할 수 있기 때문이다. 다시 말해 무작위로 실험하는 대신 머신러닝 모델로부터 제안된 분자를 사용해 실험함으로써 성공 가능성을 높일 수 있다.

분자 수준 데이터에 머신러닝을 적용하기 위해 먼저 분자 구조 데이터를 벡터로 변환하는 분자 피처화(molecular featurization)가 필요하다. 분자는 복잡한 구조를 갖기 때문에 이를 표현하기 위해 다양한 기술이 개발됐다. 분자 피처화가 끝나면 머신러닝을 진행한다.

 

분자 데이터 피처화

SMILES 문자열과 RDKit

SMILES(Simplified Molecular-Input Line-Entry System)는 분자의 화학식을 문자열로 나타내는 데 널리 사용되는 방법이다. 보통 사람에게 의미 없는 무작위 문자열처럼 보이지만, 과학자에게는 간결하고 합리적인 방식으로 원자들의 화학 결합을 표현하는 방법이다.

# thiamine
smile = ['OCCc1c(C)[n+](cs1)Cc2cnc(C)nc2N']

 

DeepChem은 분자 데이터를 다루기 위해 다른 오픈소스 라이브러리인 RDKit을 사용한다. RDKit은 SMILES 문자열에 관한 많은 기능을 갖고 있으므로 SMILES 문자열을 분자 그래프 및 다른 표현으로 변환하는 데 중요한 역할을 한다.

 

확장 연결 지문

화학 지문(chemical fingerprint)은 분자의 특성 유무를 1과 0으로 나타낸 벡터다. 확장 연결 지문(Extended-Connectivity FingerPrint)(ECFP)은 화학 지문의 몇 가지 유용한 특성을 결합한 피처화 방법으로 임의의 크기의 분자를 고정 길이의 벡터로 변환한다. 대부분의 머신러닝 모델은 입력값으로 동일한 크기의 벡터를 사용해야 하므로 확장 연결 지문은 서로 다른 크기의 분자를 갖고 동일한 학습 모델을 사용할 수 있게 해준다. 게다가 확장 연결 지문으로 변환하면 서로 비교하기도 쉽다. 계산이 빠르다는 장점도 갖고 있다.

화학 지문의 각 요소는 특정 원자의 특성 유무를 나타낸다. 확장 연결 지문 피처화는 모든 원자를 독립적으로 고려해 원자의 특성, 공유 결합의 수 등을 조사한다. 이렇게 조사된 고유한 조합을 특성이라 하며, 해당 요소가 존재하면 벡터의 해당 요소가 1의 값을 가진다. 그런 다음 피처화 알고리즘은 주변을 살펴 해당 원자와 결합된 원자를 찾고 더 큰 새로운 특성 집합을 만들어 벡터의 해당 요소로 사용한다. 피처화 알고리즘 중 가장 일반적으로 사용되는 것은 ECFP4 알고리즘이다. 그러나 확장 연결 지문에서 일부 정보는 손실된다. 예를 들어 두 개의 다른 분자가 똑같은 화학 지문을 가질 수 있기 때문에 화학 지문만 주어지면 어떤 분자에서 왔는지를 알아내는 것은 불가능하다.

smiles = ['C1CCCCC1', 'O1CCOCC1'] # cyclohexane과 dioxane
mols = [Chem.MolFromSmiles(smile) for smile in smiles]
feat = dc.feat.CircularFingerprint(size=1024)
arr = feat.featurize(mols)

 

분자 표현자

분자 데이터 기록(molecular descriptor)은 대개 분자의 구조를 설명하는 다양한 계산 값을 포함한다. RDKit 라이브러리는 분자에서 이런 값을 손쉽게 계산해준다. 상대적으로 분자의 일반적인 특성에 의존하는 것을 예측할 때 가장 잘 작동한다. 원자의 상세한 배열에 의존하는 특성을 예측할 때는 효과가 좋지 않다.

feat = dc.feat.RDKitDescriptors()
arr = feat.featurize(mols)

 

그래프 합성곱

그래프 합성곱은 일반 합성곱 신경망과 동일한 아이디어를 분자 데이터에 적용한다. 일반 합성곱 신경망(CNN)이 이미지의 각 픽셀을 벡터로 변환하는 것처럼, 그래프 합성곱은 분자 그래프의 각 노드와 에지를 벡터로 변환한다.

분자 그래프(molecular graph) : 수학적으로 분자 구조를 나타내는 방법이며, 각각의 노드(node)를 연결한 에지(edge)로 나타낸다. 이미 그래프로 표현된 데이터를 분석하는 수학적 이론은 정립돼 있기에, 분자를 그래프로 나타내는 분자 그래프는 수학적으로 분자 구조를 표현하는 유용한 방법이다.
원으로 표현된 게 노드, 선으로 표현된 게 에지이다.

그래프로 분자를 표현할 때 벡터의 요소는 원소(element), 전하(charge), 혼성화(hybridization) 등의 화학적 성질을 나타낸다. 일반 합성곱 신경망의 레이어가 입력 데이터의 부분적인 영역을 기반으로 각 픽셀에 대해 새로운 벡터를 계산하는 것처럼 그래프 합성곱 레이어도 각 노드와 에지에 대한 새로운 벡터를 출력한다. 출력값은 그래프의 각 부분에 학습된 합성곱 커널을 적용해 계산된다.

그래프 합성곱 모델은 분자 데이터를 분석하는 강력한 도구지만 분명한 한계도 있다. 분자 그래프로만 계산을 수행하다 보니 분자 구조에 대한 정보가 사라져 분자 구조와 연관된 특성은 알 수 없게 된다. 따라서 그래프 합성곱 모델은 작은 분자량의 데이터를 다룰 때는 괜찮지만 단백질과 같은 거대 분자에는 적합하지 않다.

 

용해도 예측 모델

분자의 중요한 특성인 용해도(solubility)를 예측하기 위해 실제 데이터셋으로 머신러닝을 해본다.

import deepchem as dc
from deepchem.models import GraphConvModel
from rdkit.Chem import AllChem
from rdkit import Chem

# 데이터셋 불러오기
tasks, datasets, transformers = dc.molnet.load_delaney(featurizer='GraphConv')
train_dataset, vaild_dataset, test_dataset = datasets

# featurizer = 'GraphConv' 옵션을 통해 그래프 합성곱 모델을 사용
model = GraphConvModel(n_tasks=1, mode='regression', dropout=0.2)
model.fit(train_dataset, nb_epoch=100)

 

각 샘플에 대해 단 하나의 작업(n_tasks=1), 다시 말해 하나의 출력값(용해도)을 지정한다. 그리고 회귀 모델로 만든다. 회귀 모델은 레이블이 연속적인 숫자이고 학습 모델이 가장 유사한 값을 예측한다는 것을 의미한다. 이는 각 샘플이 속한 분류 중 어느 것이 예측되는지를 판별하는 분류 모델과는 다른 개념이다.

이어서 모델의 성능을 평가하고 잘 작동하는 지 보기 위해 피어슨 상관계수를 평가 지표로 사용해본다.

피어슨 상관계수 : 두 변수 간의 관련성을 나타내는 정도다. 완전히 동일하면 +1, 전혀 다르면 0, 반대 방향으로 완전히 동일하면 -1을 가진다.

(1) 과적합된 거 같다.

학습 데이터셋에 대해 0.91, 테스트 데이터셋에 대해 0.61의 피어슨 상관계수 값이 출력된다. 책의 예시에서는 (0.95, 0.83)이 나왔을 때 심하게 과적합된 건 아니라고 했지만, 내 경우에는 과적합된 거 같다.

 

이제 완전히 새로운 분자의 용해도를 예측해보자. SMILES 문자열로 표현된 다음 다섯 개의 분자를 사용한다.

(2) from rdkit import Chem 해야 18번 줄을 실행 가능하다.

위의 무작위 분자들을 학습 모델의 입력 데이터로 사용하려면 먼저 RDKit을 사용해 SMILES 문자열을 분석(Parsing)한 다음 DeepChem으로 그래프의 합성곱의 입력 형식으로 변환한다.

이제 머신러닝 모델에 입력해 용해도를 예측할 수 있다.

(3)

SMARTS 문자열

한글 문서 치다가 특정 문자를 검색하는 것처럼 생명정보학에서도 분자 내 원자들의 특정 패턴을 찾아야 할 때가 있다. 예를 든다면…

  • 분자 데이터베이스에서 특정 구조를 포함하는 분자를 검색
  • 공통된 구조를 가진 분자들의 집합을 정렬해 시각화
  • 그림에서 하위 구조를 강조
  • 특정 구조를 제외한 계산 값이 필요할 때

SMART 문자열은 앞서 설명한 SMILES 문자열의 확장 표현으로 검색어(query)를 만드는 데 사용한다. 모든 SMILES 문자열은 SMARTS 문자열이 될 수 있다. SMILES 문자열 'CCC'는 유효한 SMARTS 문자열이며 인접한 세 개의 탄소 원자를 의미한다.

from rdkit import Chem
from rdkit.Chem.Draw import MolsToGridImage

smiles_list = ["CCCCC", "CCOCC", "CCNCC", "CCSCC"]
mol_list = [Chem.MolFromSmiles(x) for x in smiles_list]

이제 어떤 SMILES 문자열이 SMARTS 패턴 'CCC'와 일치하는지 확인할 수 있다.

query = Chem.MolFromSmarts("CCC")
match_list = [mol.GetSubstructMatch(query) for mol in mol_list]
MolsToGridImage(
  mols=mol_list, molsPerRow=4, highlightAtomLists=match_list)

(4) query : "CCC"를 찾아라

다른 분자들은 세 번 연속되는 탄소 원자가 없어서, 첫 번째 분자만 강조됐다. 다양한 방법으로 분자를 선택할 수 있다.

 

query2 = Chem.MolFromSmarts("CC")
match_list2 = [mol.GetSubstructMatch(query2) for mol in mol_list]
MolsToGridImage(
  mols=mol_list, molsPerRow=4, highlightAtomLists=match_list2)

(5) query : "CC"를 찾아라

query3 = Chem.MolFromSmarts("C*C")
match_list3 = [mol.GetSubstructMatch(query3) for mol in mol_list]
MolsToGridImage(
  mols=mol_list, molsPerRow=4, highlightAtomLists=match_list3)

(6) query : 두 탄소 원자 사이에 임의의 원자 하나가 있는 구조를 찾아라("C*C")

 

query3은 와일드카드 문자인 * 기호를 사용하여 모든 원자를 가능하게 했다.

 

query4 = Chem.MolFromSmarts("C[C,N,O]C")
match_list4 = [mol.GetSubstructMatch(query4) for mol in mol_list]
MolsToGridImage(
  mols=mol_list, molsPerRow=4, highlightAtomLists=match_list4)

(7) query : 두 개의 탄소 원자 사이에 탄소, 산소, 혹은 질소 원자 하나가 결합한 구조를 찾아라("C[C,O,N]C")

SMART 문자열에 더 자세히 알고 싶으면 http://www.daylight.com/dayhtml/doc/theory/.

 

Daylight Theory Manual

 

www.daylight.com