파이톤의 다처리로 과학 계산을 가속화하다
본 강좌에서 우리는 실제 예에서
multiprocessing
를 어떻게 사용하여 과학 계산을 가속화하는지 볼 것이다.구체적으로 Broad Institute에 게시된 공중형광현미경 이미지MCF7 Cell Painting dataset에서 모든 원자핵의 위치를 탐지합니다.이 자습서를 완료하면 다음 작업을 수행할 수 있습니다.
multiprocessing.Pool
빠른 이미지 처리데이터 세트 다운로드
Note: The imaging data is nearly 1 GB for just one plate. You will need a few GB of space to complete this tutorial.
먼저 셀 Paint 데이터 세트here의 첫 번째 보드 이미지 데이터를
images/
라는 폴더에 다운로드하여 추출합니다.TIFF 그림이 가득 담긴 images/Week1_22123/
폴더를 가져와야 합니다.그림의 이름은 다음과 같습니다.# Template
{week}_{plate}_{well}_{field}_{channel}{id}.tif
# Example
Week1_150607_B02_s4_w1EB868A72-19FC-46BB-BDFA-66B8966A817C.tif
모든 DAPI 이미지 찾기
DAPI는 각 이미지에 세포핵을 염색하는 DNA와 결합한 형광 염료다.일반적으로 DAPI 신호는 현미경의 첫 번째 통로에서 포착된다.우리는 먼저 이미지 이름을 분석하고 첫 번째 채널(
w1
에서 이미지를 찾아야 한다. 그러면 DAPI 이미지에서만 세포핵을 검출할 수 있다.from glob import glob
paths = glob('images/Week1_22123/Week1_*_w1*.tif')
paths.sort()
print(len(paths)) # => 240
여기서, 우리는 glob
와 어댑터가 있는 모드를 사용하여 모든 DAPI 이미지의 경로를 찾습니다.분명히 240개의 DAPI 이미지를 처리해야 합니다!우리는 첫 번째 그림을 불러와서 우리가 사용하고 있는 내용을 볼 수 있다.from skimage import io
import matplotlib.pyplot as plt
img = io.imread(paths[0])
print(img.shape, img.dtype, img.min(), img.max())
# => (1024, 1280) uint16 176 10016
plt.figure(figsize=(6, 6))
plt.imshow(img, cmap='gray', vmax=4000)
plt.axis('off')
plt.show()
우리는 이 그림들이 16개의 무기호 정수로 구성된 (10241280) 수조를 볼 수 있다.이 특정한 이미지의 픽셀 강도는 176에서 10016까지 다르다.
찌꺼기법으로 원자핵을 탐지하다
Scikit image는 DAPI 이미지의 세포핵을 감지하는 데 사용할 수 있는 이미지 처리를 위한 파이썬 패키지입니다.간단하고 효과적인 방법은 잡음을 줄이기 위해 그림을 매끄럽게 한 다음 주어진 강도 한도값보다 높은 국부 최대치를 찾는 것이다.이 점을 실현하기 위해 예시 이미지에 함수를 작성합시다.
from skimage.filters import gaussian
from skimage.feature import peak_local_max
def detect_nuclei(img, sigma=4, min_distance=6, threshold_abs=1000):
g = gaussian(img, sigma, preserve_range=True)
return peak_local_max(g, min_distance, threshold_abs)
centers = detect_nuclei(img)
print(centers.shape) # => (214, 2)
plt.figure(figsize=(6, 6))
plt.imshow(img, cmap='gray', vmax=4000)
plt.plot(centers[:, 1], centers[:, 0], 'r.')
plt.axis('off')
plt.show()
이제 우리는 진전이 있다!
detect_nuclei
함수는 img
수조를 받아들이고 centers
라고 불리는 원자핵(x, y) 좌표 수조를 되돌려준다.그것은 우선 img
응용gaussian
을 통해 모호하게 sigma = 4
진열을 매끄럽게 한다.preserve_range = True
매개 변수 차단skimage
은 입력한 것과 같은 강도 범위의 매끄러운 이미지g
를 되돌려줍니다.그리고 우리는 peak_local_max
함수를 사용하여 부드러운 이미지의 모든 부분 최대치를 측정합니다.우리는 두 핵센터가 실제와 부합되지 않도록 min_distance = 4
를 설치하고 이미지의 어두운 구역에서 가짜 양성이 검출되지 않도록 threshold_abs = 1000
를 설치했다.Note: We will use this
detect_nuclei
function in all subsequent experiments.
방법 1 - 입출력 이해 목록 사용
이제 원자핵 좌표를 검출하는 함수가 생겼습니다. 간단한 목록으로 이 함수를 모든 DAPI 이미지에 적용하는 것을 이해할 수 있습니다.
from tqdm.notebook import tqdm
def process_images1(paths):
return [detect_nuclei(io.imread(p)) for p in paths]
meth1_times = %timeit -n 4 -r 1 -o centers = process_images1(tqdm(paths))
# => 18 s ± 0 ns per loop (mean ± std. dev. of 1 run, 4 loops each)
process_images1
에서 우리는 이미지 경로 목록을 가져오고 목록을 사용하여 모든 이미지를 불러오고 세포핵을 검출합니다.Jupyter의 %timeit
magic 명령을 사용하면 이 방법의 평균 실행 시간이 약 18초인 것을 알 수 있다.방법2--다중처리.애아랑 같이 수영해요.
처리
Pool
를 통해 속도를 높일 수 있는지 살펴보자.이를 위해, 우리는 detect_nuclei
와 io.imread
을 함께 포장하는 함수를 정의해야 한다. 그러면 우리는 map
이미지 경로 목록에서 이 함수를 실행할 수 있다.import multiprocessing as mp
def _process_image(path):
return detect_nuclei(io.imread(path))
def process_images2(paths):
with mp.Pool() as pool:
return pool.map(_process_image, paths)
meth2_times = %timeit -n 4 -r 1 -o centers = process_images2(paths)
# => 5.54 s ± 0 ns per loop (mean ± std. dev. of 1 run, 4 loops each)
많이 좋아졌어요.이것은 cpu_count() == 8
가 달린 기계에서 운행하는 것이다.현재 같은 임무의 평균 집행 시간은 약 5.5초(>3배의 가속)이다.방법3: 기억 속의 리스트 이해
만약 우리가 세포핵을 검출하기 전에 모든 그림을 메모리에 읽는다면 어떤 일이 일어날까요?이 방법은
%timeit
명령이 실행되는 동안 디스크에서 이미지 데이터를 읽을 필요가 없기 때문에 더 빠를 것으로 추정된다.한번 해보자.import numpy as np
images = np.asarray([io.imread(p) for p in tqdm(paths)])
def process_images3(images):
return [detect_nuclei(img) for img in images]
meth3_times = %timeit -n 4 -r 1 -o centers = process_images3(tqdm(images))
# => 17.7 s ± 0 ns per loop (mean ± std. dev. of 1 run, 4 loops each)
현재 리스트의 이해 속도는 조금 빨라졌지만 약 0.3초에 불과하다.이것은 이미지 데이터를 읽는 것이 방법 1의 속도 제한 절차가 아니라 detect_nuclei
계산이라는 것을 나타낸다.방법 4 - 다중 처리.메모리 풀
완전하게 보기 위해서 메모리에 있는 모든 이미지에
multiprocessing.Pool
부터 map
까지 우리의 detect_nuclei
함수를 사용해 보겠습니다.def process_images4(images):
with mp.Pool() as pool:
return pool.map(detect_nuclei, images)
meth4_times = %timeit -n 4 -r 1 -o centers = process_images4(images)
# => 6.23 s ± 0 ns per loop (mean ± std. dev. of 1 run, 4 loops each)
잠깐만, 뭐라고?왜 이것은 multiprocessing
를 사용하여 디스크에서 그림을 읽는 것보다 느립니까?multiprocessing
모듈의 문서를 자세히 읽으면 Pool
직원에게 전달된 데이터가 pickle
를 통해 서열화되어야 한다는 것을 알 수 있습니다.이 서열화 절차는 약간의 계산 비용을 발생시킬 것이다.이것은 방법 2에 대해 우리는 경로 문자열을 처리해야 하지만, 이 예에서 우리는 전체 이미지를 처리해야 한다는 것을 의미한다.방법 5 - 다중 처리.시리얼화된 수정이 있는 풀
다행히도
multiprocessing
를 사용할 때 이런 서열화 비용을 피할 수 있는 몇 가지 방법이 있다.Mac와 Linux 시스템에서 우리는 운영체제 처리 프로세스가 갈라지는 방식을 이용하여 메모리의 대형 진열을 효율적으로 처리할 수 있다(Windows)🤷♂️). Unix 기반 시스템은 분기 프로세스에 쓰기 시 복사 행위를 사용합니다.느슨하게 말하자면, 쓸 때 복사하는 것은 지그재그 프로세스가 공유 가상 메모리를 수정하려고 할 때만 데이터를 복사하는 것을 의미한다.이 모든 것은 막후에서 발생하는데, 쓰기 즉 복제는 때때로 잠재적 공유라고 불린다.def _process_image_memory_fix(i):
global images
return detect_nuclei(images[i])
def process_images5(n):
with mp.Pool() as pool:
return pool.map(_process_image_memory_fix, range(n))
meth5_times = %timeit -n 4 -r 1 -o centers = process_images5(len(paths))
# => 5.31 s ± 0 ns per loop (mean ± std. dev. of 1 run, 4 loops each)
좋아요!이것은 방법 2의 속도보다 조금 빠르다.이 함수는 전역적으로 정의된 global
수조를 사용한다는 것을 명시하기 위해 images
문장을 사용합니다.이것은 엄격한 요구가 아니지만, 나는 _process_image_memory_fix
함수가 사용 가능한 images
수조에 달려 있다는 것을 지적하는 것이 도움이 된다는 것을 발견했다.그리고 모든 인덱스 map
를 실행하고 모든 프로세스가 images
그룹에 인덱스를 통해 필요한 이미지에 접근할 수 있도록 합니다.이런 방법은 그림 자체가 아니라 픽셀 정수만 만들 수 있다.결실
다섯 가지 방법의 평균 집행 시간을 비교해 보자.
전반적으로
multiprocessing
는 이 핵 검측 임무의 평균 집행 시간을 크게 감소시켰다.흥미로운 것은 메모리에 있는 이미지를 다중 처리하는 간단한 방법이 실제로는 평균 실행 시간을 증가시켰다는 것이다.쓰기 시 복사 행위를 이용하여 우리는 전체 이미지를 산세척할 때 발생하는 대량의 서열화 비용을 없앨 수 있다.더 많은 결과를 보여주기 위해서, 이미지마다 검출된 세포핵수에 따라 서열을 정하는 DAPI 이미지 16장의 무작위 샘플을 보여 드리겠습니다.
간단한 세포핵 검출 전략이 효과적이며 각 시야에서 관찰된 세포밀도 조직인 DAPI 이미지를 기반으로 할 수 있는 것으로 보인다.
결론
영화 속의 우주공
나는 정말 이 문장이 도움이 되기를 바란다. 그러므로 당신이 그것을 좋아한다면 나에게 알려 주십시오.여기에 소개된
multiprocessing
기술은 내가 우리의 SCOUT paper를 위해 이미지를 처리할 때 터무니없는 속도에 이르도록 도와주었다.아마도 미래에 우리는 이 예를 다시 복습할 수 있을 것이다. 그러나 GPU를 통해 속도를 가속화하면 최종적으로 가소로운 속도를 실현할 수 있을 것이다.장점: 다처리 진도표를 사용합니다.수영장.
위의
multiprocessing
예시에서 우리는 좋은 tqdm
진도표가 하나도 없다.만약 변경pool.imap
한다면 우리는 그것을 얻을 수 있다. 이것은 장시간 운행하는 계산에 매우 유용하다.def _process_image(path):
return detect_nuclei(io.imread(path))
def process_images_progress(paths):
with mp.Pool() as pool:
return list(tqdm(pool.imap(_process_image, paths), total=len(paths)))
centers = process_images_progress(paths)
도구책
이미지 세트BBBC021v1[Caie et al., Molecular Cancer Therapeutics, 2010]를 사용하여 Broad Bioimage Benchmark Collection[Ljosa et al., Nature Methods, 2012]에서 사용할 수 있습니다.
소스 가용성
본문의 모든 원본 자료는 제 블로그 GitHub repo에서 찾을 수 있습니다here.
Reference
이 문제에 관하여(파이톤의 다처리로 과학 계산을 가속화하다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/jmswaney/speeding-up-scientific-computing-with-multiprocessing-in-python-3a43텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)