成功在Windows Subsystem for Linux中,建立了Python環境、安裝必須的工具,並解決許多軟硬體的問題之後,我正式展開了我使用Python進行生物資訊分析的新篇章。
然而,上次的難題才剛解決沒有多久,還來不及迎接下一個突發狀況時,新的挑戰就如排山倒海般地淹來;而這次的意外,甚至直接造成整個WSL系統強制登出,導致分析作業被迫中斷。但是,水能覆舟,亦能載舟;它有時是發生災難,讓我心痛不已的系統,有時卻又是意外發揮功用,拯救我分析事業的系統。這中間的故事,容我為各位娓娓道來。
還沒看過第一集的,這裡有:【連結】
第一個難題:與記憶體的戰爭 – t-SNE視覺化的挑戰
t-隨機鄰近嵌入法(t-distributed Stochastic Neighbor Embedding),是一種非線性的降維技術,其主要應用在高維度數據的視覺化;以生物的資料而言,每個基因都是一個維度、每個樣本也是一個維度;當同時分別有幾萬筆基因與樣本時,就有成千上萬個維度,構成我們大腦難以直接理解的「高維度數據」。
這時候如果套用t-SNE,就能透過這種降維技術,將高維度的資訊「濃縮」到一張我們更容易理解的二維(或三維)平面圖上;降維的同時,這套方法也會高精度地保留重要的訊息,使得原本就比較相像的兩個樣本能彼此靠近;原本差異就比較大的細胞則相距較遠。這樣,我們就能初步解開癌細胞之間複雜的交互作用與模式,並使用它畫出來的二維圖表給我們帶來新的啟發。後續也可以透過演算法,查出是哪些基因造成癌症種類中的差異。

我在五月的實作中,繪製好的t-SNE圖
然而,這些高維度的資料,通常資料上的大小也非常可觀。各位可以想像如果你有一張幾萬列、幾萬行的Excel表格,然後要一次記得所有數字,同時計算它們之間的差異,一定是不可能的事情。當我們沒辦法將新的資訊放入短期記憶區,就會很有壓力,處理事情的效率也會下降;電腦也一樣,會因為記憶體過滿,導致它沒辦法再記憶新的表格內容或其他系統程序,整個系統就這樣當機了。
當時我並沒有留意到這點。我以為一份5.2 GB的表格檔案,讀進Pandas dataframe裡面也應該要是同樣的記憶體使用量,沒有想過Pandas會建立索引值(會造成額外記憶體開銷)來記憶表格的內容。於是,原本我只分配給我的WSL系統6GB的記憶體,就在短短的幾分鐘內消耗殆盡(註一),甚至系統程序沒辦法執行,因此WSL直接強制關機。
那時候我急了,因為我的筆電也沒辦法再裝更多的實體記憶體,於是我只能想辦法透過虛擬記憶體(註二)的方式,試圖用.wslconfig
檔案(如下圖)內的設定,拆儲存空間的東牆、補記憶體的西牆來拯救這場危機。

.wslconfig的示例;當中swap即為虛擬記憶體空間,可以MB或GB當作單位
我從10 GB、20 GB、一路嘗試到90 GB的虛擬記憶體,記憶體不足的問題才終於緩解;只是因為虛擬記憶體是建立在儲存空間中,一般會比實體記憶體慢很多,所以我守在電腦旁邊,一整個晚上過去了,程式碼還是執行中;讀完整個資料框了,但是卡在「將所有資料預處理」的過程。
最後,我只好跟 ChatGPT 講這件事情,請它來救救我;它提示我可以分批次讀取整個檔案,每讀完一個區塊就使用列表記下來(更省記憶體)再繼續讀取下一個部分,就不會有記憶體問題。另外也推薦給我比csv格式更有效率的parquet檔案格式(註三)。
分批次讀取的邏輯,大致如下(Python):
import pandas as pd
chunk_list = []
# 分批讀取檔案
for chunk in pd.read_csv('large_file.tsv', sep='\t', chunksize=10000):
processed_chunk = process(chunk) # 假設的資料處理函數
chunk_list.append(processed_chunk)
# 合併所有分批處理過的資料
final_df = pd.concat(chunk_list)
我霎時一驚:這些不就是我之前寫40 GB以上的資料庫時用過的技巧嗎?於是我就決定先採用分批讀取的思維試試看。結果,同樣一臺電腦,它只花用十幾分鐘就讀取csv表格完畢,而只佔據大約16 GB記憶體。
經過這次事件,我發現因為WSL如果未另外安裝某些程式的話,就不會有類似Windows 工作管理員的使用者介面,可以隨時監控記憶體用量,於是往後就建立起習慣,跑程式前、運行時都用free -h
指令來檢查記憶體用量,以及虛擬記憶體的配置是否正確等;另外也學會利用分批次讀取的思維面對大型的表格,而不是直接整個讀取。
若在Linux的終端機中執行watch -n 5 free -h
,即可輸出類似下圖的記憶體整理:

第二個難題:Windows下載生物資訊大型檔案失敗,改用WSL的命令列下載
實做生物資訊的人們,免不了要利用國外公開的資料庫來找到有用的資訊,這當中就包含美國的「癌症基因組圖譜」 (The Cancer Genome Atlas, TCGA) 。最一開始,我試著使用在Windows底下安裝的Google Chrome瀏覽器來下載壓縮檔,但是解壓縮時卻發生了「災難性的錯誤」,移到WSL中利用解壓縮的指令運行則是發現了「tar: 未預期的封存結尾 (Unexpected EOF)」。
這時候用WSL中的ls -lh
指令看這份檔案的大小,發現網站上寫這份壓縮檔有412 MB,但是下載下來的壓縮檔只有74 MB。一看到這點,再加上下載檔案速度真的很慢,我就懷疑應該是連線逾時導致瀏覽器沒辦法給我完整的檔案。
於是,這個時候我從美國國家癌症研究所 (National Cancer Institute, NCI) 上找到了他們提供的命令列工具:GDC Data Transfer Tool並在WSL中安裝;然後改用manifest格式檔案作為該程式判斷下載檔案的依據。
注意:以下是bash指令,且WSL中安裝的是Ubuntu 24.04 LTS。
cd ~
wget https://gdc.cancer.gov/files/public/file/gdc-client_v1.6.1_Ubuntu_x64.zip
unzip gdc-client_v1.6.1_Ubuntu_x64.zip
chmod +x gdc-client
./gdc-client download -m manifest.txt -d ~/blca
這樣,當我從TCGA上面擷取到BLCA癌症的manifest檔案時,它就會自己從資料庫下載這些資料到我指定的~/blca資料夾中!
不過,有時候事情總是一波未平、一波又起;在我使用這些指令下載資料的時候,命令列突然出現了104: ECONNRESET,而且還是好幾十筆資料都因為這樣而無法下載,變成之後我要用wget來補。
問過ChatGPT之後,讓我有了新的發現:很多人都跟我遇到過一樣的問題,但原因很簡單:像是TCGA這種美國的國家級資料庫,幾乎都有預防駭客攻擊的機制,例如防止分散式阻斷服務攻擊 (Distributed Denial-of-Service, DDoS) 的防火牆來預防伺服器崩潰,一旦遇到有人使用數十或數百個連線來下載檔案,即認定是攻擊而自動重設連線。
而GDC Data Transfer Tool 預設是開啟許多個連線下載這些檔案,這在一個終端機分頁中倒是還好;但是如果同時開很多個終端機做一樣的事情(只是換成不同的癌症種類)就恰好達到「數十或數百個連線」的阻斷條件,於是資料就因為連線重置而無法正常下載。
知道這些之後,我只能乖乖地把分頁關到只剩下一個,並且補上-t 1
的指令讓它一個檔案只開一條連線;用慢慢下載的策略來防止連線被重置。這次還是有104: ECONNRESET,但是機率大幅減少,要用wget補上的功夫也就省下了一大半。
對我而言,WSL這次的神救援無疑又給我上了幾堂寶貴的課程:如何冷靜地判斷應該採用的策略,而不是卡在瀏覽器下載失敗的問題本身;以及網路層的安全機制與共處技巧。
下集預告:WSL的大搬家!正式移轉至原生Ubuntu 24.04系統的兩大挑戰
如果喜歡這個系列,請喜歡這篇文章;每一個喜歡,都是我繼續分享這些故事的動力。
(註一)
WSL的預設設定會占用Windows一半的實體記憶體;當時我在Windows端裝有12 GB的記憶體,因此系統判定分配 6 GB。
(註二)
在我的這篇案例中,虛擬記憶體是透過「用磁碟空間來擴充實體記憶體」來實現的;如果要看更多介紹,詳見:維基百科
(註三,本註解由 Google Gemini 生成)
Parquet是一種欄式儲存格式(Columnar Storage Format),主要用於在大數據處理和分析中儲存和查詢數據。 它是一種高效的檔案格式,相對於傳統的行式儲存格式(Row-based Storage),可以更有效地進行壓縮和讀取特定欄位,其通常被用於各種大數據處理框架。