CNN #1 RNA interference 효율 예측 모델

CNN #1 RNA interference 효율 예측 모델 #

#2026-02-27


개요 #

siRNA 서열을 입력받아서 RNA interference 효율을 예측하는 회귀 모델

  • siRNA 길이: 21bp
  • 위치당 염기 종류: 4
  • -> input shape: (21,4)

#

모델 구조

features = tf.keras.Input(shape=(21, 4))
prev = features

for i in range(2):
    prev = layers.Conv1D(
        filters=10,
        kernel_size=10,
        activation=tf.nn.relu,
        padding='same'
    )(prev)
    prev = layers.Dropout(rate=0.3)(prev)

output = layers.Dense(
    units=1,
    activation=tf.math.sigmoid
)(layers.Flatten()(prev))

keras_model = tf.keras.Model(inputs=features, outputs=output)
keras_model.summary()
  • 21bp siRNA sequence를 입력받고
  • 두 개의 Conv1D layer로 RNAi 효율 관련 염기 패턴을 학습하고
  • 0~1 사이의 효율값을 예측.

요약하면 이렇다.

21bp siRNA one-hot sequence
→ Conv1D
→ Dropout
→ Conv1D
→ Dropout
→ Flatten
→ Dense(sigmoid)
→ RNAi efficiency prediction

#

첫번째 Conv1D layer #

prev = layers.Conv1D(
    filters=10,
    kernel_size=10,
    activation=tf.nn.relu,
    padding='same'
)(prev)

Conv1D 필터가 siRNA 서열을 왼쪽에서 오른쪽으로 훑으면서 염기 조합 패턴을 찾는다

  • kernel_size=10: 필터 한 번에 10개 염기
  • filters=10: 이런 패턴 탐지기를 10개 만든다
  • activation=tf.nn.relu: Conv1D 결과를 비선형적으로 바꾼다 즉 음수 값은 0으로 만들고, 양수 값은 그대로 통과시킨다.
  • padding=‘same’: convolution을 해도 sequence 길이를 유지한다. 입력 길이가 21이면 출력도 길이 21로 유지된다.
conv1d (Conv1D)             (None, 21, 10)            410

첫번째 Conv1D 결과

  • 입력은 (None, 21, 4)이고 출력은 (None, 21, 10)이다.
    • 각 siRNA 샘플마다 21개 위치에 대해 10개의 필터 반응값을 갖는다.
  • 첫 번째 Conv1D의 parameter 수는 410
    • 첫 번째 Conv1D는 one-hot encoding 차원이 4이기 때문에 입력 채널이 4개이다
    • 필터 하나는 kernel_size=10만큼의 위치를 보고, 각 위치마다 4개 채널을 본다.
    • 그래서 필터 하나의 weight 수는 10 × 4 = 40
    • 그리고 필터마다 bias가 1개 있어서 40 + 1 = 41
    • 필터가 총 10개이므로 전체 parameter 수는 41 × 10 = 410

#

Dropout layer #

prev = layers.Dropout(rate=0.3)(prev)
  • Dropout: 학습 중에 일부 뉴런 출력을 랜덤하게 꺼버리는 정규화 기법
  • rate=0.3: 학습할 때 약 30%의 값을 임시로 0으로 만든다.
dropout (Dropout)           (None, 21, 10)            0

Dropout 결과

  • Dropout은 parameter가 없다
    • Dropout은 학습되는 weight가 있는 layer가 아니라, 학습 중 일부 값을 꺼주는 동작만 하기 때문에 parameter 수가 0이다.

#

두번째 Conv1D layer #

prev = layers.Conv1D(
    filters=10,
    kernel_size=10,
    activation=tf.nn.relu,
    padding='same'
)(prev)

코드는 같음.

  • 첫 번째 Conv1D가 찾은 저수준 sequence pattern들을 다시 조합해서 더 복합적인 패턴을 찾는다.
  • 첫 번째 Conv1D가 이런 수준이라면
    • 특정 10bp 구간에 G/C-rich 패턴이 있는가?
    • 특정 위치 조합이 있는가?
    • 특정 motif 비슷한 신호가 있는가?
  • 두 번째 Conv1D는 이런 식이다.
    • 앞 layer에서 감지한 여러 motif 조합이 특정 위치 관계로 나타나는가?
    • 여러 부분 패턴이 함께 나타날 때 RNAi 효율이 높아지는가?
conv1d_1 (Conv1D)           (None, 21, 10)            1010

두번째 Conv1D 결과

  • 입력 shape는 (None, 21, 4)가 아니라 (None, 21, 10)
  • 출력 shape은 그대로 (None, 21, 10)
    • padding=‘same’이므로 길이 21은 유지
    • filters=10이므로 채널 수도 10
  • parameter 수는 1010
    • 첫 번째 Conv1D가 10개의 filter 출력을 만들었기 때문에 두 번째 Conv1D의 입력 채널 수는 4 -> 10으로 바뀜
    • 필터 하나의 weight 수는 kernel_size × input_channels = 10 × 10 = 100
    • bias 1개를 더하면 필터 하나당 101
    • 필터가 10개이므로 전체 parameter 수는 101 × 10 = 1010

#

Flatten layer #

layers.Flatten()(prev)
  • Conv1D를 거친 후 데이터 shape은 (None, 21, 10)
  • Dense layer는 1차원 벡터를 입력으로 받기 때문에 2차원 feature map을 한 줄로 펼쳐서 (None, 21, 10) -> (None, 210)로 만든다.
  • 한 샘플당
    • 21개 위치마다 10개의 feature -> 210개의 feature가 된다.
flatten (Flatten)           (None, 210)               0

Flatten 결과

  • Flatten은 단순히 모양만 바꾸는 layer라서 parameter가 없다.

#

Dense output layer #

output = layers.Dense(
    units=1,
    activation=tf.math.sigmoid
)(layers.Flatten()(prev))
  • 210개의 feature를 받아서 최종 예측값 1개를 출력
dense (Dense)               (None, 1)                 211

Dense 결과

  • parameter 수는 211
    • 입력 feature가 210개이고, 출력 unit이 1개
    • weight = 210 × 1 = 210 / bias = 1 / total = 211

Activation으로 sigmoid를 사용

  • 출력값은 0과 1 사이로 제한된다.
    • 0에 가까움 -> RNAi 효율 낮음
    • 1에 가까움 -> RNAi 효율 높음
  • 결과: siRNA sequence를 보고 RNAi 효율 score를 예측하는 회귀 모델

#

모델 Summary #

Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, 21, 4)]           0         
                                                                 
 conv1d (Conv1D)             (None, 21, 10)            410       
                                                                 
 dropout (Dropout)           (None, 21, 10)            0         
                                                                 
 conv1d_1 (Conv1D)           (None, 21, 10)            1010      
                                                                 
 dropout_1 (Dropout)         (None, 21, 10)            0         
                                                                 
 flatten (Flatten)           (None, 210)               0         
                                                                 
 dense (Dense)               (None, 1)                 211       
                                                                 
=================================================================
Total params: 1631 (6.37 KB)
Trainable params: 1631 (6.37 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________

전체 parameter 수

  • 첫 번째 Conv1D: 410
  • 두 번째 Conv1D: 1010
  • Dense: 211
  • 총합: 410 + 1010 + 211 = 1631

-> 총 1,631개의 학습 가능한 parameter를 가진 작은 CNN 모델. 길이 21의 짧은 siRNA sequence를 다루기 때문에, Conv1D 두 층만으로도 sequence pattern을 어느 정도 학습할 수 있다.

#

학습 #

rnai_model = dc.models.KerasModel(
    keras_model,
    loss=dc.models.losses.L2Loss(),
    batch_size=1000,
    model_dir='rnai'
)
  • 손실함수: L2 loss
    • RNAi 효율 예측은 classification이 아니라 연속값 예측에 가깝다 따라서 binary cross entropy가 아니라 예측 RNAi 효율값과 실제 RNAi 효율값의 차이를 줄이는 회귀 손실을 사용한다.
  • batch_size=1000: 한 번에 최대 1000개 샘플씩 묶어서 학습.
metric_rnai = dc.metrics.Metric(dc.metrics.pearsonr, mode='regression')

평가 metric: Pearson correlation

  • 예측값과 실제값이 얼마나 선형적으로 잘 같이 움직이는지.
    • +1에 가까움: 예측값과 실제값이 매우 잘 일치하는 방향으로 움직임
    • 0에 가까움: 거의 관계 없음
    • -1에 가까움: 반대로 움직임
  • RNAi 효율 예측에서는 정확히 값이 완전히 같냐보다 효율이 높은 siRNA는 높게 예측하고 효율이 낮은 siRNA는 낮게 예측하는가가 중요해서 Pearson r이 적절함.
for i in range(20):
    rnai_model.fit(train_rnai, nb_epoch=10)

    train_score = rnai_model.evaluate(
        train_rnai,
        [metric_rnai]
    )['pearsonr']

    valid_score = rnai_model.evaluate(
        valid_rnai,
        [metric_rnai]
    )['pearsonr']

    print(
        f'[RNAi] epoch {(i+1)*10:3d} | '
        f'train pearsonr: {train_score:.4f} | '
        f'valid pearsonr: {valid_score:.4f}'
    )

학습 loop

  • 20번 반복, 한 번 반복할 때마다 nb_epoch=10으로 학습
  • 전체 학습 epoch 수는 20번 반복 × 10 epoch = 총 200 epoch.
  • Epoch마다
    • train_rnai 데이터로 10 epoch 학습
    • train_rnai에서 Pearson r 계산
    • valid_rnai에서 Pearson r 계산
    • 현재 epoch, train score, valid score 출력

#

결과 #

[RNAi] epoch  10 | train pearsonr: 0.0863 | valid pearsonr: 0.1463
[RNAi] epoch  20 | train pearsonr: 0.3657 | valid pearsonr: 0.3439
[RNAi] epoch  30 | train pearsonr: 0.4876 | valid pearsonr: 0.4309
[RNAi] epoch  40 | train pearsonr: 0.5318 | valid pearsonr: 0.4699
[RNAi] epoch  50 | train pearsonr: 0.5669 | valid pearsonr: 0.4991
[RNAi] epoch  60 | train pearsonr: 0.6044 | valid pearsonr: 0.5267
[RNAi] epoch  70 | train pearsonr: 0.6275 | valid pearsonr: 0.5464
[RNAi] epoch  80 | train pearsonr: 0.6438 | valid pearsonr: 0.5598
[RNAi] epoch  90 | train pearsonr: 0.6559 | valid pearsonr: 0.5693
[RNAi] epoch 100 | train pearsonr: 0.6666 | valid pearsonr: 0.5780
[RNAi] epoch 110 | train pearsonr: 0.6750 | valid pearsonr: 0.5875
[RNAi] epoch 120 | train pearsonr: 0.6816 | valid pearsonr: 0.5925
[RNAi] epoch 130 | train pearsonr: 0.6887 | valid pearsonr: 0.6027
[RNAi] epoch 140 | train pearsonr: 0.6942 | valid pearsonr: 0.6079
[RNAi] epoch 150 | train pearsonr: 0.6988 | valid pearsonr: 0.6111
[RNAi] epoch 160 | train pearsonr: 0.7038 | valid pearsonr: 0.6148
[RNAi] epoch 170 | train pearsonr: 0.7071 | valid pearsonr: 0.6183
[RNAi] epoch 180 | train pearsonr: 0.7125 | valid pearsonr: 0.6210
[RNAi] epoch 190 | train pearsonr: 0.7151 | valid pearsonr: 0.6220
[RNAi] epoch 200 | train pearsonr: 0.7191 | valid pearsonr: 0.6261

train Pearson r과 valid Pearson r이 모두 꾸준히 증가

  • 10 epoch: train pearsonr: 0.0863 / valid pearsonr: 0.1463
  • 20 epoch: train pearsonr: 0.3657 / valid pearsonr: 0.3439
  • 30 epoch: train pearsonr: 0.4876 / valid pearsonr: 0.4309
  • -> 처음에는 랜덤한 필터에 가까웠던 Conv1D filter들이 학습을 거치면서 RNAi 효율과 관련된 sequence pattern을 점점 잡아가고 있다.

train과 valid 성능 차이

  • 최종 200 epoch 결과: train pearsonr: 0.7191 / valid pearsonr: 0.6261
  • train 성능이 valid 성능보다 높지만 valid Pearson r이 마지막까지 대체로 증가중이므로 검증 데이터에 대해서도 일반화 성능이 개선되고 있음.

최종 valid Pearson r = 0.6261

  • 예측값과 실제 RNAi 효율 사이에 중간 이상 정도의 양의 상관관계.
  • 모델이 완벽하게 맞히는 것은 아니지만, 효율이 높은 siRNA와 낮은 siRNA를 어느 정도 구분할 수 있다
  • RNAi 효율은 sequence만으로 100% 결정되는 것이 아닐 수 있다. 실제 생물학적 환경에서는 target accessibility, RNA secondary structure, GC content, off-target 가능성, 세포 상태, 실험 noise 같은 다양한 요인이 영향을 줄 수 있다.
  • 따라서 단순히 21bp one-hot sequence만 사용한 작은 CNN 모델이 validation Pearson r 0.626 정도를 냈다면, sequence 자체에 RNAi 효율을 설명하는 정보가 꽤 들어 있다는 뜻으로 볼 수 있다.

#

모델 아키텍처 #

image