salmon으로 전사체 정량 — 유사정렬·RNA-seq 업스트림

TL;DRsalmon은 RNA-seq 리드를 전통 정렬(STAR→BAM) 없이 유사정렬(quasi-mapping / selective alignment)로 전사체(transcript)에 확률적으로 배정해, 카운트와 TPM을 빠르고 가볍게 산출하는 도구입니다. 흐름은 두 단계로 단순합니다. 전사체 참조(예 GENCODE·Ensembl cDNA)로 salmon index를 만들고, salmon quant -i index -l A -1 -2 ...로 정량하면 quant.sf 한 파일에 전사체별 Name·Length·EffectiveLength·TPM·NumReads가 떨어집니다.

이 글은 재현이 쉬운 공개 데이터(Arabidopsis thaliana TAIR10 cDNA)로 conda 설치부터 인덱싱·정량·결과 해석까지 한 줄씩 따라갑니다. 곧이어 decoy-aware index (유전체를 미끼로), GC·서열 편향 보정 (--gcBias·--seqBias), bootstrap 불확실성 추정까지 실전 옵션을 붙이고, 같은 계열인 kallisto와 한 줄로 비교합니다.

마지막으로 quant.sf(전사체 수준)를 tximport로 받아 유전자 수준 카운트로 집계해 DESeq2 같은 차등발현 분석으로 넘기는, 업스트림→다운스트림 연결까지 짚습니다.

🔗 관련 글  ·  salmon 결과를 R로 받아 유전자 수준으로 집계하는 다음 단계는 tximport로 정량 불러오기에서 이어집니다  ·  유사정렬을 우회하는 전통 정렬은 BWA-MEM·STAR로 리드 정렬하기에서 다뤘습니다  ·  TPM이 뭔지·왜 카운트와 다른지는 RNA-seq 정규화란?RNA-seq 정량 단위 완전 정리에서 정리했습니다  ·  집계한 카운트로 차등발현을 보는 DESeq2로 DEG 분석하기로 마무리됩니다

이 글에서 만들 것

RNA-seq 업스트림은 대체로 두 갈래로 나뉩니다. 하나는 리드를 유전체(genome)에 정렬(align)해 BAM을 만들고 거기서 유전자마다 겹친 리드를 세는 정렬 기반(STAR→featureCounts) 길이고, 다른 하나는 정렬을 건너뛰고 리드가 어느 전사체에서 왔는지 곧장 배정해 카운트를 뽑는 유사정렬 기반(salmon·kallisto) 길입니다. salmon은 뒤쪽 길의 사실상 표준이죠. 무거운 BAM을 만들지 않아 빠르고 가볍고, 전사체 수준으로 바로 정량한다는 게 핵심입니다.

이 글은 그 salmon 길을 처음부터 끝까지 만듭니다. 구체적으로 ① conda로 salmon을 깔고, ② 전사체 참조(A. thaliana cDNA)로 salmon index를 만들고, ③ salmon quant로 리드를 정량해 quant.sf를 얻고, ④ 결과의 다섯 컬럼(Name·Length·EffectiveLength·TPM·NumReads)을 읽고, ⑤ decoy-aware index·--gcBias·bootstrap 같은 실전 옵션을 붙인 뒤, ⑥ tximport로 유전자 카운트까지 잇습니다. 명령은 salmon 1.10+ 기준입니다.

전통 정렬과 뭐가 다른지 한눈에 보면 이렇습니다. STAR는 리드마다 유전체 위 정확한 좌표를 찾아 BAM에 적지만, salmon은 리드가 어느 전사체 집합과 맞는지만 빠르게 확인하고(quasi-mapping), 여러 전사체에 걸치는 리드(다중매핑)는 EM (expectation–maximization) 알고리즘으로 확률적으로 나눠 배정합니다. 좌표를 일일이 적지 않으니 훨씬 가볍습니다.

# 핵심 흐름 한눈에 (전사체 참조 + paired-end 리드 기준)
salmon index -t transcripts.fa.gz -i salmon_index -k 31        # ① 전사체 색인
salmon quant -i salmon_index -l A \
  -1 reads_1.fastq.gz -2 reads_2.fastq.gz \
  --validateMappings -p 8 -o quant_out                         # ② 유사정렬 정량
head quant_out/quant.sf                                        # ③ 결과 (Name·TPM·NumReads…)
# ↓ 이후 R에서 tximport로 quant.sf → 유전자 카운트 → DESeq2
그림 1. RNA-seq 업스트림 속 salmon — 전통 정렬(STAR→BAM→featureCounts)을 우회해, 전사체 참조에 유사정렬로 바로 정량한 뒤 tximport·DESeq2로 잇는다.

1. 사전 준비 — 설치와 예제 데이터

salmon을 설치합니다. bioconda 채널의 conda가 가장 편하고 버전 고정도 쉽습니다. Docker 이미지(combinelab/salmon)나 미리 컴파일된 바이너리도 있지만, 여기서는 conda로 통일합니다.

# conda (bioconda) — 권장, 전용 환경에 격리 설치
conda create -n salmon -c bioconda -c conda-forge salmon
conda activate salmon
# 설치 확인 (버전 출력)
salmon --version
salmon 1.10.2

이제 실습 데이터를 받습니다. 재현이 쉽도록 salmon 공식 튜토리얼과 같은 소형 공개 데이터, Arabidopsis thaliana (애기장대)의 TAIR10 cDNA를 씁니다. 애기장대는 유전체가 작아(약 135 Mb) 전사체 참조도 가벼워, 노트북에서도 몇 분 안에 색인·정량이 끝납니다.

# 전사체 참조(cDNA) 내려받기 — Ensembl Plants
curl -o athal.fa.gz \
  http://ftp.ensemblgenomes.org/pub/plants/release-57/fasta/arabidopsis_thaliana/cdna/Arabidopsis_thaliana.TAIR10.cdna.all.fa.gz

# 리드(FASTQ)는 소형 공개 샘플을 쓴다 (여기서는 SRR 예시)
#   실데이터라면 SRA Toolkit의 fasterq-dump로 받거나, 튜토리얼 제공 리드를 사용

전사체 참조가 어떻게 생겼는지 잠깐 봅니다. FASTA 형식이라, > 로 시작하는 헤더 한 줄(전사체 이름)과 서열 줄이 번갈아 나옵니다.

# 참조 앞부분 확인 — 전사체 헤더(>AT...)와 서열
zcat athal.fa.gz | head -3
>AT1G01010.1 cdna chromosome:TAIR10:1:3631:5899:1 gene:AT1G01010
ATGGAGGATCAGGTGATGAGGACAAGTCCGTTCTCACTCTTTTCCTCAAACTTCTTCCGT
GTCCAAGATCTGATGGAAGACCGGTTCAGCTTCTTGCCTCCATTCTCTGCTTCTTGTTGT

핵심은 salmon이 이 전사체 서열 자체를 참조로 쓴다는 점입니다. 유전체(genome)가 아니라 전사체(transcriptome)에 리드를 맞추므로, 인트론·스플라이싱을 신경 쓰지 않고 곧장 "이 리드가 어느 전사체에서 나왔나"를 묻습니다. 그래서 스플라이스-인지 정렬기(STAR)가 필요 없죠.

2. salmon index — 전사체 참조 색인

정량 전에 참조를 색인해야 합니다. salmon index는 전사체 서열로 빠른 검색 구조를 만들어, 리드마다 어느 전사체와 맞는지를 순식간에 찾게 합니다. 색인은 한 번만 만들면 같은 참조를 쓰는 모든 샘플에 재사용합니다.

# 전사체 색인 만들기
#   -t: 전사체 FASTA (필수) · -i: 색인 출력 폴더 · -k: k-mer 길이
salmon index -t athal.fa.gz -i athal_index -k 31

-k는 색인의 최소 매칭 단위인 k-mer 길이입니다. 공식 문서 권장대로 75 bp 이상 리드에는 -k 31이 무난합니다. 리드가 아주 짧으면(예 <50 bp) k를 줄여야 민감도가 올라갑니다. 색인이 끝나면 폴더 안에 여러 이진 파일이 생기는데, 직접 열어 볼 일은 없고 salmon quant가 알아서 읽습니다.

여기서 실전 팁 하나. 전사체만 색인하면, 실은 유전체의 다른 곳(예 인트론·비번역 영역)에서 온 리드가 억지로 전사체에 배정돼 카운트를 부풀릴 수 있습니다. 이를 막는 게 뒤(5절)에서 다룰 decoy-aware index — 유전체 전체를 미끼(decoy)로 함께 색인해, 전사체보다 유전체에 더 잘 맞는 리드를 걸러내는 방식입니다. 정확도를 중시하면 decoy-aware를 권장하고, 개념을 익히는 지금 단계에선 전사체만으로 충분합니다.

3. salmon quant — 유사정렬로 정량

이제 색인에 리드를 흘려 정량합니다. salmon quant가 리드를 전사체에 유사정렬하고, 다중매핑을 EM으로 나눠 배정한 뒤, 전사체마다 추정 카운트(NumReads)와 정규화값(TPM)을 계산합니다.

# paired-end 정량
#   -i: 색인 폴더 · -l A: 라이브러리 타입 자동 추론
#   -1 -2: 정방향·역방향 리드(짝 있음) · --validateMappings: 선택정렬(권장)
#   -p: 스레드 · -o: 출력 폴더
salmon quant -i athal_index -l A \
  -1 reads_1.fastq.gz -2 reads_2.fastq.gz \
  --validateMappings -p 8 -o athal_quant

옵션을 하나씩 봅니다. -l A는 라이브러리 타입(가닥 특이성·리드 방향)을 salmon이 앞쪽 수천 개 리드로 자동 추론하게 합니다. 직접 알면 ISR처럼 명시할 수도 있지만, 대개 A로 두면 됩니다. --validateMappings는 유사매핑 후보를 실제 서열 정렬로 한 번 검증하는 선택정렬(selective alignment)로, salmon 1.x의 사실상 기본이자 권장 모드입니다(가짜 매핑을 줄여 정확도를 올립니다). 단일 리드(single-end)면 -1 -2 대신 -r reads.fastq.gz를 씁니다.

정량이 끝나면 로그에 매핑률(mapping rate)이 찍힙니다. 이 값이 유난히 낮으면(예 30% 미만) 참조·리드 불일치나 라이브러리 문제를 의심합니다.

# 로그에서 매핑률 확인 (salmon이 표준에러로 출력)
# 예시 출력의 한 줄:
[info] Mapping rate = 89.4212%

출력 폴더 athal_quant/에는 여러 파일이 생기는데, 핵심은 quant.sf입니다. 사람이 읽을 수 있는 탭 구분 표로, 전사체마다 한 줄씩 정량 결과가 담깁니다.

# 결과 표 앞부분 — 5개 컬럼
head athal_quant/quant.sf
Name	Length	EffectiveLength	TPM	NumReads
AT1G01010.1	1688	1438.52	4.213	42.000
AT1G01020.1	1774	1524.51	11.874	125.300
AT1G01020.2	832	583.017	0.000	0.000
AT1G01030.1	1905	1655.51	2.087	23.000
AT1G01040.1	6251	6001.51	18.442	795.117

다섯 컬럼의 뜻은 이렇습니다.

컬럼
Name전사체 이름(참조 FASTA 헤더의 첫 토큰)
Length전사체 길이(bp)
EffectiveLength유효 길이 — 단편 길이 분포·편향을 보정한 "실질적으로 리드가 시작될 수 있는 자리 수"
TPMtranscripts per million — 깊이·길이를 보정한 상대 발현량, 샘플 내 합이 100만
NumReads그 전사체에 배정된 추정 리드 수(정수가 아닐 수 있음 — EM이 확률적으로 쪼갬)

여기서 두 가지가 중요합니다. 첫째, NumReads는 소수일 수 있습니다. 한 리드가 여러 전사체에 맞으면 EM이 그 리드를 확률에 따라 0.6개·0.4개처럼 나눠 배정하기 때문이죠. 둘째, TPM은 이미 정규화된 값이라 차등발현 분석에 그대로 넣으면 안 됩니다. DESeq2·edgeR 같은 도구는 원시(raw) 카운트를 전제로 자체 정규화·통계 모형을 돌리므로, TPM이 아니라 NumReads (정확히는 tximport가 넘겨주는 카운트)를 써야 합니다. TPM과 카운트의 차이는 RNA-seq 정량 단위 편에서 자세히 풀었습니다.

EffectiveLength가 Length보다 짧은 건, 전사체 양 끝에서는 정해진 단편 길이만큼 리드가 시작될 자리가 줄어들기 때문입니다. TPM은 NumReads를 이 유효 길이로 나눠 계산하므로, 길이 보정이 여기서 이뤄집니다.

그림 2. 유사정렬 vs 전통 정렬 — 전통 정렬은 리드마다 유전체 위 좌표를 확정해 BAM에 적고, salmon은 리드가 맞는 전사체 집합만 찾아 다중매핑을 EM으로 확률 배정한다.

4. 자주 발생하는 에러 & 해결

  • 매핑률이 바닥 — 참조·리드 종 불일치 — 매핑률이 몇 % 수준이면 대개 참조 전사체와 리드의 종·버전이 안 맞는 경우입니다. 사람 리드를 애기장대 cDNA에 맞추면 당연히 거의 안 붙죠. 리드가 어느 종·유전체 빌드에서 왔는지 확인하고, 같은 종의 cDNA (가급적 같은 어셈블리 버전, 예 GENCODE·Ensembl 동일 릴리스)를 참조로 씁니다. gDNA (유전체 서열)가 아니라 cDNA/transcriptome FASTA인지도 확인하세요 — salmon은 전사체에 맞추는 도구입니다.
  • -l 라이브러리 타입 오지정-l을 손으로 잘못 넣으면(예 실제로는 역가닥인데 SF로 지정) 카운트가 크게 어긋납니다. 확신이 없으면 -l A로 두어 salmon이 추론하게 하고, 로그의 Automatically detected most likely library type = ... 줄로 추론 결과를 확인합니다. 짝(paired) 리드는 반드시 -1·-2로 정방향·역방향을 나눠 주고, 순서가 바뀌지 않게 합니다.
  • 압축·경로 문제 — salmon은 gzip된 FASTQ(.fastq.gz)를 그대로 읽으니 미리 풀 필요가 없습니다. 오히려 풀어서 넘기면 디스크만 낭비하죠. 다만 파일 경로에 공백·한글이 있거나 리드 파일이 손상(잘린 gzip)되면 실패하니, -1·-2 파일 개수와 순서가 정확히 짝을 이루는지 확인합니다.
  • decoy 없이 부풀려진 카운트 — 전사체만 색인하면 유전체 다른 곳에서 온 리드가 비슷한 전사체에 억지로 배정돼 일부 유전자 카운트가 부풀 수 있습니다. 정확도가 중요하면 5절의 decoy-aware index로 유전체를 미끼로 함께 색인해, 전사체보다 유전체에 더 잘 맞는 리드를 정량에서 빼세요.
  • tximport에서 전사체–유전자 매핑 누락quant.sf는 전사체 수준이라, 유전자 수준으로 올리려면 "전사체 ID ↔ 유전자 ID" 대응표(tx2gene)가 필요합니다. 이게 없거나 ID 표기(버전 번호 .1 유무 등)가 어긋나면 tximport가 매핑에 실패합니다. 참조 FASTA와 같은 출처의 GTF에서 대응표를 만들고, 버전 접미사를 참조와 일치시키세요.

5. 확장 — 더 해 볼 것

decoy-aware index (유전체를 미끼로) — 가장 권장되는 실전 색인법입니다. 유전체 FASTA를 전사체 뒤에 이어 붙인 gentrome을 만들고, 유전체 서열 이름을 decoys.txt로 넘겨 함께 색인합니다. decoy로 지정된 서열은 정량 출력엔 안 나오지만, 리드가 전사체보다 유전체에 더 잘 맞으면 그쪽으로 배정돼 가짜 카운트를 줄여 줍니다.

# 1) decoy 이름 목록 만들기 (유전체 FASTA의 서열 이름)
grep '^>' genome.fa | sed 's/>//; s/ .*//' > decoys.txt
# 2) 전사체 + 유전체를 이어 gentrome 만들기 (전사체가 앞, 유전체가 뒤)
cat transcripts.fa genome.fa | gzip > gentrome.fa.gz
# 3) decoy-aware 색인
salmon index -t gentrome.fa.gz -d decoys.txt -i salmon_decoy_index -k 31

편향 보정 (--gcBias·--seqBias) — 실데이터에는 GC 함량·서열 문맥에 따른 증폭·시퀀싱 편향이 있습니다. salmon quant--gcBias(GC 편향)와 --seqBias(무작위 프라이머 등 서열 편향)를 붙이면 이를 모델링해 보정합니다. 계산이 약간 더 들지만, 여러 샘플을 비교하는 분석에선 붙이는 편이 안전합니다.

salmon quant -i salmon_decoy_index -l A \
  -1 reads_1.fastq.gz -2 reads_2.fastq.gz \
  --validateMappings --gcBias --seqBias -p 8 -o quant_biascorr

bootstrap으로 불확실성 추정 (--numBootstraps) — 전사체 정량은 다중매핑 때문에 본질적으로 추정치입니다. --numBootstraps 100을 주면 리샘플링으로 100벌의 대체 추정을 만들어, 각 전사체 추정의 분산(불확실성)을 함께 냅니다. 이 정보는 sleuth 같은 전사체 수준 차등발현 도구가 활용합니다.

  • kallisto — 같은 계열 한 줄 비교 — kallisto도 유사정렬 계열로, 전사체에 리드를 확률 배정해 카운트·TPM을 냅니다. 인터페이스도 닮았죠(kallisto index -i idx transcripts.fakallisto quant -i idx -o out r1.fq r2.fq). 차이라면 kallisto는 de Bruijn 그래프 기반 pseudoalignment, salmon은 접미사 배열 기반 quasi-mapping + 선택정렬과 GC·서열 편향 보정을 기본 제공한다는 점입니다. 실전 결과는 대체로 비슷하고, 편향 보정·decoy가 필요하면 salmon을 고르는 편입니다.
  • tximport로 유전자 수준 집계quant.sf(전사체 수준)를 R의 tximport로 읽어 유전자 수준 카운트 행렬로 올리는 게 표준 다음 단계입니다. tximport는 전사체 길이 정보를 오프셋으로 함께 넘겨, DESeq2·edgeR가 길이 편향까지 감안하게 해 줍니다. 구체적 코드는 tximport 편에서 다뤘고, 그렇게 만든 카운트로 차등발현을 보는 흐름은 DESeq2 편으로 이어집니다.
# R — quant.sf 여러 개를 유전자 카운트로 (개념 스케치)
library(tximport)
txi <- tximport(files, type = "salmon", tx2gene = tx2gene)  # 전사체→유전자
# txi$counts 를 DESeqDataSetFromTximport()로 넘겨 DESeq2 분석
그림 3. quant.sf 다섯 컬럼과 도구 비교 — salmon·kallisto는 유사정렬로 전사체를 바로 정량하고(빠름·경량), STAR+featureCounts는 유전체 정렬 후 카운트를 센다.

다음 학습

🎓 이어서 읽기
(다음 단계) tximport로 정량 불러오기 — 이 글에서 만든 quant.sf를 R로 받아 유전자 수준 카운트로 집계
(정량 단위) RNA-seq 정량 단위 완전 정리 — TPM·RPKM·FPKM·CPM, 왜 TPM을 DESeq2에 넣으면 안 되나
(정규화 개념) RNA-seq 정규화란? — 읽은 수를 비교 가능한 값으로 바꾸는 원리
(전통 정렬) BWA-MEM·STAR로 리드 정렬하기 — salmon이 우회하는 유전체 정렬 경로
(하류 분석) DESeq2로 DEG 분석하기 — 집계한 카운트로 차등발현·볼케이노까지

References

  1. Patro, R., Duggal, G., Love, M. I., Irizarry, R. A., & Kingsford, C. (2017). Salmon provides fast and bias-aware quantification of transcript expression. Nature Methods, 14(4), 417–419. (salmon 원논문)
  2. Bray, N. L., Pimentel, H., Melsted, P., & Pachter, L. (2016). Near-optimal probabilistic RNA-seq quantification. Nature Biotechnology, 34(5), 525–527. (kallisto 원논문)
  3. Soneson, C., Love, M. I., & Robinson, M. D. (2015). Differential analyses for RNA-seq: transcript-level estimates improve gene-level inferences. F1000Research, 4, 1521. (tximport 원논문)
  4. Srivastava, A., Malik, L., Sarkar, H., et al. (2020). Alignment and mapping methodology influence transcript abundance estimation. Genome Biology, 21, 239. (선택정렬·decoy-aware 방법론)
  5. COMBINE-lab. (2024). Salmon documentation — index · quant · decoy-aware · gcBias · numBootstraps. salmon.readthedocs.io
  6. Love, M. I., Soneson, C., & Patro, R. (2018). Swimming downstream: statistical analysis of RNA-seq expression data (tximport·tximeta). F1000Research, 7, 952. (salmon→tximport→DESeq2 워크플로)

Pipette & Pipeline · A bio portfolio journal

이 글을 쓴 사람 Yumingming

생명융합공학과 박사과정.
Microbiome · Cosmetics · RNA Therapeutics · Bioinformatics를 공부하며,
실험(Wet Lab)과 데이터(Dry Lab)를 잇는 글을 논문(article) 기반으로 씁니다.

About · 더 알아보기 →

⚕️ 이 글은 학습·정보 제공 목적이며, 의학적 진단·치료·조언을 대체하지 않습니다. 건강·질병·치료에 관한 결정은 반드시 의사 등 전문가와 상의하세요. 자세한 내용은 면책조항을 참고해 주세요.