淺析細(xì)胞圖像數(shù)據(jù)的主動(dòng)學(xué)習(xí)
通過細(xì)胞圖像的標(biāo)簽對(duì)模型性能的影響,為數(shù)據(jù)設(shè)置優(yōu)先級(jí)和權(quán)重。
許多機(jī)器學(xué)習(xí)任務(wù)的主要障礙之一是缺乏標(biāo)記數(shù)據(jù)。而標(biāo)記數(shù)據(jù)可能會(huì)耗費(fèi)很長的時(shí)間,并且很昂貴,因此很多時(shí)候嘗試使用機(jī)器學(xué)習(xí)方法來解決問題是不合理的。
為了解決這個(gè)問題,機(jī)器學(xué)習(xí)領(lǐng)域出現(xiàn)了一個(gè)叫做主動(dòng)學(xué)習(xí)的領(lǐng)域。主動(dòng)學(xué)習(xí)是機(jī)器學(xué)習(xí)中的一種方法,它提供了一個(gè)框架,根據(jù)模型已經(jīng)看到的標(biāo)記數(shù)據(jù)對(duì)未標(biāo)記的數(shù)據(jù)樣本進(jìn)行優(yōu)先排序。如果想
細(xì)胞成像的分割和分類等技術(shù)是一個(gè)快速發(fā)展的領(lǐng)域研究。就像在其他機(jī)器學(xué)習(xí)領(lǐng)域一樣,數(shù)據(jù)的標(biāo)注是非常昂貴的,并且對(duì)于數(shù)據(jù)標(biāo)注的質(zhì)量要求也非常的高。針對(duì)這一問題,本篇文章介紹一種對(duì)紅細(xì)胞和白細(xì)胞圖像分類任務(wù)的主動(dòng)學(xué)習(xí)端到端工作流程。
我們的目標(biāo)是將生物學(xué)和主動(dòng)學(xué)習(xí)的結(jié)合,并幫助其他人使用主動(dòng)學(xué)習(xí)方法解決生物學(xué)領(lǐng)域中類似的和更復(fù)雜的任務(wù)。
本篇文主要由三個(gè)部分組成:
- 細(xì)胞圖像預(yù)處理——在這里將介紹如何預(yù)處理未分割的血細(xì)胞圖像。
- 使用CellProfiler提取細(xì)胞特征——展示如何從生物細(xì)胞照片圖像中提取形態(tài)學(xué)特征,以用作機(jī)器學(xué)習(xí)模型的特征。
- 使用主動(dòng)學(xué)習(xí)——展示一個(gè)模擬使用主動(dòng)學(xué)習(xí)和不使用主動(dòng)學(xué)習(xí)的對(duì)比實(shí)驗(yàn)。
細(xì)胞圖像預(yù)處理
我們將使用在MIT許可的血細(xì)胞圖像數(shù)據(jù)集(GitHub和Kaggle)。每張圖片都根據(jù)紅細(xì)胞(RBC)和白細(xì)胞(WBC)分類進(jìn)行標(biāo)記。對(duì)于這4種白細(xì)胞(嗜酸性粒細(xì)胞、淋巴細(xì)胞、單核細(xì)胞和中性粒細(xì)胞)還有附加的標(biāo)簽,但在本文的研究中沒有使用這些標(biāo)簽。
下面是一個(gè)來自數(shù)據(jù)集的全尺寸原始圖像的例子:
創(chuàng)建樣本DF
原始數(shù)據(jù)集包含一個(gè)export.py腳本,它將XML注釋解析為一個(gè)CSV表,其中包含每個(gè)細(xì)胞的文件名、細(xì)胞類型標(biāo)簽和邊界框。
原始腳本沒有包含cell_id列,但我們要對(duì)單個(gè)細(xì)胞進(jìn)行分類,所以我們稍微修改了代碼,添加了該列并添加了一列包括image_id和cell_id的filename列:
import os, sys, randomimport xml.etree.ElementTree as ETfrom glob import globimport pandas as pdfrom shutil import copyfileannotations = glob('BCCD_Dataset/BCCD/Annotations/*.xml')df = []for file in annotations:#filename = file.split('/')[-1].split('.')[0] + '.jpg'#filename = str(cnt) + '.jpg'filename = file.split('\\')[-1]filename =filename.split('.')[0] + '.jpg'row = []parsedXML = ET.parse(file)cell_id = 0for node in parsedXML.getroot().iter('object'):blood_cells = node.find('name').textxmin = int(node.find('bndbox/xmin').text)xmax = int(node.find('bndbox/xmax').text)ymin = int(node.find('bndbox/ymin').text)ymax = int(node.find('bndbox/ymax').text)row = [filename, cell_id, blood_cells, xmin, xmax, ymin, ymax]df.append(row)cell_id += 1data = pd.DataFrame(df, columns=['filename', 'cell_id', 'cell_type', 'xmin', 'xmax', 'ymin', 'ymax'])data['image_id'] = data['filename'].apply(lambda x: int(x[-7:-4]))data[['filename', 'image_id', 'cell_id', 'cell_type', 'xmin', 'xmax', 'ymin', 'ymax']].to_csv('bccd.csv', index=False)
裁剪
為了能夠處理數(shù)據(jù),第一步是根據(jù)邊界框坐標(biāo)裁剪全尺寸圖像。這就產(chǎn)生了很多大小不一的細(xì)胞圖像:
裁剪的代碼如下:
import osimport pandas as pdfrom PIL import Imagedef crop_cell(row):"""crop_cell(row)given a pd.Series row of the dataframe, load row['filename'] with PIL,crop it to the box row['xmin'], row['xmax'], row['ymin'], row['ymax']save the cropped image,return cropped filename"""input_dir = 'BCCD\JPEGImages'output_dir = 'BCCD\cropped'# open imageim = Image.open(f"{input_dir}\{row['filename']}")# size of the image in pixelswidth, height = im.size# setting the points for cropped imageleft = row['xmin']bottom = row['ymax']right = row['xmax']top = row['ymin']# cropped imageim1 = im.crop((left, top, right, bottom))cropped_fname = f"BloodImage_{row['image_id']:03d}_{row['cell_id']:02d}.jpg"# shows the image in image viewer# im1.show()# save imagetry:im1.save(f"{output_dir}\{cropped_fname}")except:return 'error while saving image'return cropped_fnameif __name__ == "__main__":# load labels csv into Pandas DataFramefilepath = "BCCD\dataset2-master\labels.csv"df = pd.read_csv(filepath)# iterate through cells, crop each cell, and save cropped cell to filedataset_df['cell_filename'] = dataset_df.apply(crop_cell, axis=1)
以上就是我們所做的所有預(yù)處理操作?,F(xiàn)在,我們繼續(xù)使用CellProfiler提取特征。
使用CellProfiler提取細(xì)胞特征
CellProfiler是一個(gè)免費(fèi)的開源圖像分析軟件,可以從大規(guī)模細(xì)胞圖像中自動(dòng)定量測(cè)量。CellProfiler還包含一個(gè)GUI界面,允許我們可視化的操作
首先下載CellProfiler,如果CellProfiler無法打開,則可能需要安裝Visual C ++發(fā)布包,具體安裝方式參考官網(wǎng)。
打開軟件就可以加載圖像了, 如果想構(gòu)建管道可以在CellProfiler官網(wǎng)找到其提供的可用的功能列表。 大多數(shù)功能分為三個(gè)主要組:圖像處理,目標(biāo)的處理和測(cè)量。
常用的功能如下:
圖像處理 - 轉(zhuǎn)為灰度圖:
對(duì)象目標(biāo)處理 - 識(shí)別主要對(duì)象
測(cè)量 - 測(cè)量對(duì)象強(qiáng)度
CellProfiler可以將輸出為CSV文件或者保存指定數(shù)據(jù)庫中。這里我們將輸出保存為CSV文件,然后將其加載到Python進(jìn)行進(jìn)一步處理。
說明:CellProfiler還可以將你處理圖像的流程保存并進(jìn)行分享。
主動(dòng)學(xué)習(xí)
我們現(xiàn)在已經(jīng)有了訓(xùn)練需要的搜有數(shù)據(jù),現(xiàn)在可以開始試驗(yàn)使用主動(dòng)學(xué)習(xí)策略是否可以通過更少的數(shù)據(jù)標(biāo)記獲得更高的準(zhǔn)確性。 我們的假設(shè)是:使用主動(dòng)學(xué)習(xí)可以通過大量減少在細(xì)胞分類任務(wù)上訓(xùn)練機(jī)器學(xué)習(xí)模型所需的標(biāo)記數(shù)據(jù)量來節(jié)省寶貴的時(shí)間和精力。
主動(dòng)學(xué)習(xí)框架
在深入研究實(shí)驗(yàn)之前,我們希望對(duì)modAL進(jìn)行快速介紹: modAL是Python的活躍學(xué)習(xí)框架。 它提供了Sklearn API,因此可以非常容易的將其集成到代碼中。 該框架可以輕松地使用不同的主動(dòng)學(xué)習(xí)策略。 他們的文檔也很清晰,所以建議從它開始你的一個(gè)主動(dòng)學(xué)習(xí)項(xiàng)目。
主動(dòng)學(xué)習(xí)與隨機(jī)學(xué)習(xí)
為了驗(yàn)證假設(shè),我們將進(jìn)行一項(xiàng)實(shí)驗(yàn),將添加新標(biāo)簽數(shù)據(jù)的隨機(jī)子抽樣策略與主動(dòng)學(xué)習(xí)策略進(jìn)行比較。開始用一些相同的標(biāo)記樣本訓(xùn)練2個(gè)Logistic回歸估計(jì)器。然后將在一個(gè)模型中使用隨機(jī)策略,在第二個(gè)模型中使用主動(dòng)學(xué)習(xí)策略。
我們首先為實(shí)驗(yàn)準(zhǔn)備數(shù)據(jù),加載由Cell Profiler言創(chuàng)建的特征。 這里過濾了無色血細(xì)胞的血小板,只保留紅和白細(xì)胞(將問題簡(jiǎn)化,并減少數(shù)據(jù)量) 。所以現(xiàn)在我們正在嘗試解決二進(jìn)制分類問題 - RBC與WBC。使用Sklearn Label的label encoder進(jìn)行編碼,并拆分?jǐn)?shù)據(jù)集進(jìn)行訓(xùn)練和測(cè)試。
# imports for the whole experimentimport numpy as npfrom matplotlib import pyplot as pltfrom modAL import ActiveLearnerimport pandas as pdfrom modAL.uncertainty import uncertainty_samplingfrom sklearn import preprocessingfrom sklearn.metrics import , average_precision_scorefrom sklearn.linear_model import LogisticRegression# upload the cell profiler features for each celldata = pd.read_csv('Zaretski_Image_All.csv')# filter plateletsdata = data[data['cell_type'] != 'Platelets']# define the labeltarget = 'cell_type'label_encoder = preprocessing.LabelEncoder()y = label_encoder.fit_transform(data[target])# take the learning features onlyX = data.iloc[:, 5:]# create training and testing setsX_train, X_test, y_train, y_test = train_test_split(X.to_numpy(), y, test_size=0.33, random_state=42)
下一步就是創(chuàng)建模型
dummy_learner = LogisticRegression()
active_learner = ActiveLearner(
estimator=LogisticRegression(),
query_strategy=uncertainty_sampling()
)
dummy_learner是使用隨機(jī)策略的模型,而active_learner是使用主動(dòng)學(xué)習(xí)策略的模型。為了實(shí)例化一個(gè)主動(dòng)學(xué)習(xí)模型,我們使用modAL包中的ActiveLearner對(duì)象。在“estimator”字段中,可以插入任何sklearnAPI兼容的模型。在query_strategy '字段中可以選擇特定的主動(dòng)學(xué)習(xí)策略。這里使用“uncertainty_sampling()”。這方面更多的信息請(qǐng)查看modAL文檔。
將訓(xùn)練數(shù)據(jù)分成兩組。第一個(gè)是訓(xùn)練數(shù)據(jù),我們知道它的標(biāo)簽,會(huì)用它來訓(xùn)練模型。第二個(gè)是驗(yàn)證數(shù)據(jù),雖然標(biāo)簽也是已知的,但是我們假裝不知道它的標(biāo)簽,并通過模型預(yù)測(cè)的標(biāo)簽和實(shí)際標(biāo)簽進(jìn)行比較來評(píng)估模型的性能。然后我們將訓(xùn)練的數(shù)據(jù)樣本數(shù)設(shè)置成5。
# the training size that we will start withbase_size = 5# the 'base' data that will be the training set for our modelX_train_base_dummy = X_train[:base_size]X_train_base_active = X_train[:base_size]y_train_base_dummy = y_train[:base_size]y_train_base_active = y_train[:base_size]# the 'new' data that will simulate unlabeled data that we pick a sample from and label itX_train_new_dummy = X_train[base_size:]X_train_new_active = X_train[base_size:]y_train_new_dummy = y_train[base_size:]y_train_new_active = y_train[base_size:]
我們訓(xùn)練298個(gè)epoch,在每個(gè)epoch中,將訓(xùn)練這倆個(gè)模型和選擇下一個(gè)樣本,并根據(jù)每個(gè)模型的策略選擇是否將樣本加入到我們的“基礎(chǔ)”數(shù)據(jù)中,并在每個(gè)epoch中測(cè)試其準(zhǔn)確性。因?yàn)榉诸愂遣黄胶獾?,所以使用平均精度評(píng)分來衡量模型的性能。
在隨機(jī)策略中選擇下一個(gè)樣本,只需將下一個(gè)樣本添加到虛擬數(shù)據(jù)集的“新”組中,這是因?yàn)閿?shù)據(jù)集已經(jīng)是打亂的的,因此不需要在進(jìn)行這個(gè)操作。對(duì)于主動(dòng)學(xué)習(xí),將使用名為“query”的ActiveLearner方法,該方法獲取“新”組的未標(biāo)記數(shù)據(jù),并返回他建議添加到訓(xùn)練“基礎(chǔ)”組的樣本索引。被選擇的樣本都將從組中刪除,因此樣本只能被選擇一次。
# arrays to accumulate the scores of each simulation along the epochsdummy_scores = []active_scores = []# number of desired epochsrange_epoch = 298# running the experimentfor i in range(range_epoch):# train the models on the 'base' datasetactive_learner.fit(X_train_base_active, y_train_base_active)dummy_learner.fit(X_train_base_dummy, y_train_base_dummy)# evaluate the modelsdummy_pred = dummy_learner.predict(X_test)active_pred = active_learner.predict(X_test)# accumulate the scoresdummy_scores.append(average_precision_score(dummy_pred, y_test))active_scores.append(average_precision_score(active_pred, y_test))# pick the next sample in the random strategy and randomly# add it to the 'base' dataset of the dummy learner and remove it from the 'new' datasetX_train_base_dummy = np.append(X_train_base_dummy, [X_train_new_dummy[0, :]], axis=0)y_train_base_dummy = np.concatenate([y_train_base_dummy, np.array([y_train_new_dummy[0]])], axis=0)X_train_new_dummy = X_train_new_dummy[1:]y_train_new_dummy = y_train_new_dummy[1:]# pick next sample in the active strategyquery_idx, query_sample = active_learner.query(X_train_new_active)# add the index to the 'base' dataset of the active learner and remove it from the 'new' datasetX_train_base_active = np.append(X_train_base_active, X_train_new_active[query_idx], axis=0)y_train_base_active = np.concatenate([y_train_base_active, y_train_new_active[query_idx]], axis=0)X_train_new_active = np.concatenate([X_train_new_active[:query_idx[0]], X_train_new_active[query_idx[0] + 1:]], axis=0)y_train_new_active = np.concatenate([y_train_new_active[:query_idx[0]], y_train_new_active[query_idx[0] + 1:]], axis=0)
結(jié)果如下:
plt.plot(list(range(range_epoch)), active_scores, label='Active Learning')plt.plot(list(range(range_epoch)), dummy_scores, label='Dummy')plt.xlabel('number of added samples')plt.ylabel('average precision score')plt.legend(loc='lower right')plt.savefig("models robustness vs dummy.png", bbox_inches='tight')plt.show()
策略之間的差異還是很大的,可以看到主動(dòng)學(xué)習(xí)只使用25個(gè)樣本就可以達(dá)到平均精度0.9得分! 而使用隨機(jī)的策略則需要175個(gè)樣本才能達(dá)到相同的精度!
此外主動(dòng)學(xué)習(xí)策略的模型的分?jǐn)?shù)接近0.99,而隨機(jī)模型的分?jǐn)?shù)在0.95左右停止了! 如果我們使用所有數(shù)據(jù),那么它們最終分?jǐn)?shù)是相同的,但是我們的研究目的是在少量標(biāo)注數(shù)據(jù)的前提下訓(xùn)練,所以只使用了數(shù)據(jù)集中的300個(gè)隨機(jī)樣本。
總結(jié)
本文展示了將主動(dòng)學(xué)習(xí)用于細(xì)胞成像任務(wù)的好處。主動(dòng)學(xué)習(xí)是機(jī)器學(xué)習(xí)中的一組方法,可根據(jù)其標(biāo)簽對(duì)模型性能的影響來優(yōu)先考慮未標(biāo)記的數(shù)據(jù)示例的解決方案。由于標(biāo)記數(shù)據(jù)是一項(xiàng)涉及許多資源(金錢和時(shí)間)的任務(wù),因此判斷那些標(biāo)記那些樣本可以最大程度地提高模型的性能是非常必要的。
細(xì)胞成像為生物學(xué),醫(yī)學(xué)和藥理學(xué)領(lǐng)域做出了巨大貢獻(xiàn)。以前分析細(xì)胞圖像需要有價(jià)值的專業(yè)人力資本,但是像主動(dòng)學(xué)習(xí)這種技術(shù)的出現(xiàn)為醫(yī)學(xué)領(lǐng)域這種需要大量人力標(biāo)注數(shù)據(jù)集的領(lǐng)域提供了一個(gè)非常好的解決方案。