Image

Как классифицировать подтип рака легких по количеству копий ДНК с помощью PyTorch

Пошаговое введение в понимание рака с точки зрения специалиста по анализу данных.

Делиться

6ac6de2a0ba02cb42a8441d904aec881

В этой статье я покажу, как построить свёрточную нейронную сеть, способную различать типы рака, используя простой классификатор PyTorch. Данные и код, использованные для обучения, находятся в открытом доступе, и обучение можно провести на персональном компьютере, возможно, даже на центральном процессоре.

Рак — это досадный побочный эффект накопления информационных ошибок в клетках на протяжении жизни, что приводит к неконтролируемому росту. В качестве исследователей мы изучаем закономерности этих ошибок, чтобы лучше понять это заболевание. С точки зрения специалиста по данным, геном человека представляет собой строку длиной около трёх миллиардов букв, состоящую из букв A, C, G и T (то есть 2 бита информации на букву). Ошибка копирования или внешнее событие могут потенциально удалить/вставить/изменить букву, вызывая мутацию и потенциальное нарушение функции генома.

Однако отдельные ошибки практически никогда не приводят к развитию рака. В организме человека существует множество механизмов, предотвращающих развитие рака, включая специальные белки — так называемые опухолевые супрессоры. Для того чтобы клетка могла обеспечить устойчивый рост, необходимо выполнение ряда условий — так называемых «признаков рака».

9e4690471f3a7c86238f4500200d2de4

Следовательно, изменений в отдельных буквах ДНК обычно недостаточно для запуска самоподдерживающегося пролиферативного роста. Подавляющее большинство видов рака, вызванных мутациями (в отличие от других источников рака, например, вируса ВПЧ), также демонстрируют изменения числа копий (ЧК). Это масштабные события, часто сопровождающиеся добавлением или удалением миллионов оснований ДНК одновременно.

4f5a815fffefcd7ecae390257688c5ae

Эти обширные изменения структуры генома приводят к потере генов, препятствующих развитию рака, и накоплению генов, способствующих росту клеток. Секвенирование ДНК этих клеток позволяет выявить эти изменения, что довольно часто происходит в областях, специфичных для данного типа рака. Значения числа копий для каждого аллеля можно получить из данных секвенирования с помощью генераторов числа копий.

Обработка профилей номеров копий

Одно из преимуществ работы с профилями числа копий (CN) заключается в том, что они не являются биометрическими и, следовательно, могут быть опубликованы без ограничений доступа. Это позволяет нам накапливать данные из различных исследований с течением времени для создания наборов данных достаточного размера. Однако данные, полученные в ходе разных исследований, не всегда можно напрямую сравнивать, поскольку они могут быть получены с использованием разных технологий, иметь разное разрешение или быть предварительно обработаны разными способами.

Для получения данных, их совместной обработки и визуализации мы будем использовать инструмент CNSistent, разработанный в рамках работы Института вычислительной биологии рака Университетской клиники г. Кельн, Германия.

Сначала мы клонируем репозиторий и данные и устанавливаем версию, используемую в этом тексте:

git clone [email protected]:schwarzlab/cnsistent.git cd cnsistent git checkout v0.9.0

Поскольку данные, которые мы будем использовать, находятся внутри репозитория (около 1 ГБ), загрузка займёт несколько минут. Для клонирования в системе должны быть установлены как Git, так и Git LFS.

Внутри репозитория находится файл requirements.txt, в котором перечислены все зависимости, которые можно установить с помощью pip install -r requirements.txt.

(Рекомендуется сначала создать виртуальную среду.) После установки всех необходимых компонентов CNSistent можно установить, выполнив команду pip install -e . в той же папке. Флаг -e устанавливает пакет из исходного каталога, что необходимо для доступа к данным через API.

Репозиторий содержит необработанные данные из трёх наборов данных: TCGA, PCAWG и TRACERx. Их необходимо предварительно обработать. Это можно сделать, запустив скрипт bash ./scripts/data_process.sh.

Теперь мы обработали наборы данных и можем загрузить их с помощью библиотеки утилит данных CNSistent:

импортируйте cns.data_utils как cdu samples_df, cns_df = cdu.main_load(«imp») print(cns_df.head())

Получаем следующий результат:

| | sample_id | chrom | начало | конец | major_cn | minor_cn | |—:|:————|:———|———:|———:|————:|————:| | 0 | SP101724 | chr1 | 0 | 27256755 | 2 | 2 | | 1 | SP101724 | chr1 | 27256755 | 28028200 | 3 | 2 | | 2 | SP101724 | chr1 | 28028200 | 32976095 | 2 | 2 | | 3 | SP101724 | chr1 | 32976095 | 33354394 | 5 | 2 | | 4 | SP101724 | chr1 | 33354394 | 33554783 | 3 | 2 |

В этой таблице приведены данные о количестве копий со следующими столбцами:

  • sample_id: идентификатор образца,
  • хром: хромосома,
  • начало: начальная позиция сегмента (с индексом 0 включительно),
  • конец: конечная позиция сегмента (исключая 0-индексную),
  • major_cn: количество копий основного аллеля (большего из двух),
  • minor_cn: количество копий минорного аллеля (меньший из двух).

Таким образом, на первой строке мы видим сегмент, указывающий на то, что образец SP101724 имеет 2 копии основного аллеля и 2 копии минорного аллеля (всего 4) в области хромосомы 1 от 0 до 27,26 мегабаз.

Второй загруженный нами фрейм данных, samples_df, содержит метаданные образцов. Для наших целей важен только тип. Мы можем просмотреть доступные типы, выполнив:

импортируйте matplotlib.pyplot как plt type_counts = sampling_df[«type»].value_counts() plt.figure(figsize=(10, 6)) type_counts.plot(kind='bar') plt.ylabel('Count') plt.xticks(rotation=90)

82114c7b633dd0e518eb1d344d0ce7ba

В приведённом выше примере мы видим потенциальную проблему с данными: длина отдельных сегментов неравномерна. Первый сегмент имеет длину 27,26 мегабаз, а второй — всего 0,77 мегабаз. Это проблема для нейронной сети, которая ожидает, что входные данные будут фиксированного размера.

Технически мы могли бы взять все существующие точки останова и создать сегменты между ними в наборе данных, так называемая минимально согласованная сегментация. Однако это привело бы к огромному количеству сегментов — быстрая проверка с помощью len(cns_df[“end”].unique()) показывает, что существует 823652 уникальные точки останова.

В качестве альтернативы, мы можем использовать CNSistent для создания новой сегментации с помощью алгоритма биннинга. Это создаст сегменты фиксированного размера, которые можно использовать в качестве входных данных для нейронной сети. В нашей работе мы определили сегменты размером 1–3 мегабазы, обеспечивающие наилучший компромисс между точностью и переобучением. Сначала мы создаём сегментацию, а затем применяем её для получения новых CNS-файлов с помощью следующего Bash-скрипта:

threads=8 cns segment whole —out «./out/segs_3MB.bed» —split 3000000 —remove gaps — filter 300000 для набора данных в TRACERx PCAWG TCGA_hg19; do cns aggregate ./out/${dataset}_cns_imp.tsv — segments ./out/segs_3MB.bed — out ./out/${dataset}_bin_3MB.tsv — samples ./out/${dataset}_samples.tsv — threads $threads done

Цикл обрабатывает каждый набор данных отдельно, сохраняя при этом ту же сегментацию. Флаг —threads используется для ускорения процесса за счёт параллельного выполнения агрегации, корректируя значение в зависимости от количества доступных ядер.

Аргументы —remove gaps —filter 300000 удаляют области с низкой отображаемостью (также известные как пробелы) и отфильтровывают сегменты короче 300 КБ. Аргумент —split 3000000 создаёт сегменты размером 3 МБ.

Немелкоклеточный рак легкого

В данной статье мы сосредоточимся на классификации немелкоклеточного рака лёгкого, на долю которого приходится около 85% всех случаев рака лёгкого, в частности на различии между аденокарциномой и плоскоклеточным раком. Важно различать эти два вида рака, поскольку схемы их лечения будут различаться, а новые методы дают надежду на неинвазивную диагностику с помощью образцов крови или мазков из носа.

Мы используем полученные выше сегменты и загрузим их с помощью предоставленной функции полезности. Поскольку мы классифицируем рак по двум типам, мы можем отфильтровать выборки, включив только соответствующие типы: LUAD (аденокарцинома) и LUSC (плоскоклеточный рак), и построить график для первой выборки:

импорт cns samples_df, cns_df = cdu.main_load(«3MB») samples_df = sample_df.query(«введите ['LUAD', 'LUSC']») cns_df = cns.select_CNS_samples(cns_df, samples_df) cns_df = cns.only_aut(cns_df) cns.fig_lines(cns.cns_head(cns_df, n=3))

Сегменты числа основных и второстепенных копий в бинах по 3 МБ для первых трёх образцов. В данном случае все три образца получены в результате многорегионального секвенирования одного и того же пациента, что демонстрирует, насколько гетерогенными могут быть раковые клетки даже в пределах одной опухоли.

Модель сверточной нейронной сети

Для запуска кода требуется установленный Python 3 с PyTorch 2+ и оболочка, совместимая с Bash. Для более быстрого обучения рекомендуется использовать графический процессор NVIDIA, но это не обязательно.

Сначала мы определим сверточную нейронную сеть с тремя слоями:

импортируйте torch.nn как nn класс CNSConvNet(nn.Module): def __init__(self, num_classes): super(CNSConvNet, self).__init__() self.conv_layers = nn.Sequential( nn.Conv1d(входящие_каналы=2, выходные_каналы=16, размер_ядра=3, заполнение=1), nn.ReLU(), nn.MaxPool1d(размер_ядра=2), nn.Conv1d(входящие_каналы=16, выходные_каналы=32, размер_ядра=3, заполнение=1), nn.ReLU(), nn.MaxPool1d(размер_ядра=2), nn.Conv1d(входящие_каналы=32, выходные_каналы=64, размер_ядра=3, заполнение=1), nn.ReLU(), nn.MaxPool1d(kernel_size=2) ) self.fc_layers = nn.Sequential( nn.LazyLinear(128), nn.ReLU(), nn.Dropout(0.5), nn.Linear(128, num_classes) ) def forward(self, x): x = self.conv_layers(x) x = x.view(x.size(0), -1) x = self.fc_layers(x) return x

Это шаблонная глубокая сверточная нейронная сеть с двумя входными каналами — по одному на каждый аллель — и тремя свёрточными слоями с одномерным ядром размера 3 и функцией активации ReLU. За свёрточными слоями следуют слои максимального пулинга с размером ядра 2. Свёртка традиционно используется для обнаружения краёв, что полезно для нас, поскольку нас интересуют изменения числа копий, то есть краёв сегментов.

Выходные данные свёрточных слоёв затем сглаживаются и пропускаются через два полносвязных слоя с дропаутом. LazyLinearlayer объединяет выходные данные 64 каналов в один слой из 128 узлов, без необходимости вычисления количества узлов в конце свёртки. Именно там находится большинство наших параметров, поэтому мы также применяем дропаут для предотвращения переобучения.

Обучение модели

Сначала нам нужно преобразовать данные из фреймов данных в тензоры Torch. Мы используем вспомогательную функцию bins_to_features, которая создаёт трёхмерный массив признаков нужного формата (выборки, аллели, сегменты). В процессе мы также разбиваем данные на обучающий и тестовый наборы в соотношении 4:1:

import torch from torch.utils.data import TensorDataset, DataLoader from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split # конвертация данных в признаки и метки features, samples_list, columns_df = cns.bins_to_features(cns_df) # конвертация данных в тензоры Torch X = torch.FloatTensor(features) label_encoder = LabelEncoder() y = torch.LongTensor(label_encoder.fit_transform(samples_df.loc[samples_list][«type»])) # Разделение тест/обучение X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0) # Создание загрузчиков данных train_loader = DataLoader(TensorDataset(X_train, y_train), batch_size=32, shuffle=True) test_loader = DataLoader(TensorDataset(X_test, y_test), batch_size=32, shuffle=False)

Теперь мы можем обучить модель, используя следующий цикл обучения с 20 эпохами. Оптимизатор Adam и алгоритм потерь CrossEntropy обычно используются для задач классификации, поэтому мы используем их и здесь:

# настройка модели, потерь и оптимизатора device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = CNSConvNet(num_classes=len(label_encoder.classes_)).to(device) criteria = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # Обучающий цикл num_epochs = 20 for epoch in range(num_epochs): model.train() running_loss = 0.0 for inputs, labels in train_loader: inputs, labels = inputs.to(device), labels.to(device) # Очистить градиенты optimizer.zero_grad() # Прямой проход outputs = model(inputs) loss = criteria(outputs, labels) # Обратный проход и оптимизация loss.backward() optimizer.step() running_loss += loss.item() # Печать статистики print(f'Эпоха {epoch+1}/{num_epochs}, Потеря: {running_loss/len(train_loader):.4f}')

На этом обучение завершено. После этого мы можем оценить модель и распечатать матрицу ошибок:

import numpy as np from sklearn.metrics import confused_matrix import seaborn as sns # Перебираем партии в тестовом наборе и собираем прогнозы model.eval() y_true = [] y_pred = [] with torch.no_grad(): for inputs, labels in test_loader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) y_true.extend(labels.cpu().numpy()) y_pred.extend(outputs.argmax(dim=1).cpu().numpy()) _, predicted = torch.max(outputs.data, 1) # Рассчитываем точность и матрицу путаницы precision = (np.array(y_true) == np.array(y_pred)).mean() cm = confused_matrix(y_true, y_pred) # Построить график матрица неточностей plt.figure(figsize=(3, 3), dpi=200) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=label_encoder.classes_, yticklabels=label_encoder.classes_) plt.xlabel('Predicted') plt.ylabel('True') plt.title('Матрица неточностей, precision={:.2f}'.format(accuracy)) plt.savefig(«confusion_matrix.png», bbox_inches='tight')

970ec1b219344e3cd35aa15dc48358c5

Процесс обучения занимает в общей сложности около 7 секунд на графическом процессоре NVIDIA RTX 4090.

Заключение

Мы разработали эффективный и точный классификатор подтипов рака лёгкого на основе данных о числе копий. Как мы показали, такие модели хорошо переносятся в новые исследования и источники данных о последовательностях.

Массовый ИИ часто оправдывают, в частности, как «решение проблемы рака». Однако, как и в этой статье, небольшие модели с классическими подходами обычно хорошо справляются со своей задачей. Некоторые даже утверждают, что настоящее препятствие машинному обучению в биологии и медицине заключается не в решении проблем, а в реальном влиянии на пациентов.

Тем не менее, машинное обучение в целом смогло решить как минимум одну важнейшую загадку вычислительной биологии, вновь привлекая внимание к машинному обучению в области лечения рака. При некоторой удаче мы, возможно, сможем считать следующее десятилетие временем, когда мы наконец «решили» проблему рака.

Бонус: Cell2Sentence

Современные основополагающие модели часто содержат информацию, например, о том, какие гены имеют высокую копийность в тех или иных видах рака, в составе обучающей выборки. Это, однако, привело к созданию подходов, основанных на LLM, таких как Cell2Sentence, где набор данных преобразуется в текст на естественном языке и отправляется на LLM.

Мы можем использовать этот подход здесь. Сначала мы преобразуем случайную выборку LUSC в ранжированный список генов на основе их копийности:

импорт cns импорт cns.data_utils как cdu samples_df, cns_df = cdu.main_load(«COSMIC») cns_df = cns.add_total_cn(cns_df) lusc_df = cns.select_cns_by_type(cns_df, samples_df, «LUSC») lusc_one_df = cns.cns_head(lusc_df, 1) rank_list = lusc_one_df.sort_values(by=»total_cn», ascending=False)[«name»].to_list()

Затем мы преобразуем список генов в запрос на естественном языке:

query = «Ниже представлен список генов COSMIC {gene_names}, упорядоченный по убыванию общего числа копий в ячейке. Ваша задача — определить тип рака в этом образце.nn» query += «{gene_names}: » + » «.join(rank_list) + «nn» query += «Ответ:» print(query)

Представленная в ChatGPT 5 модель правильно идентифицирует рак LUSC:

Исходя из указанного вами профиля числа копий генов, наиболее убедительным признаком является массивная амплификация 11q13 (CCND1, FADD) вместе с SOX2, PIK3CA, TP63, EGFR, FGFR1, MYC, CCND1/CCND2/CCND3, CDK6, CDK4. Это очень характерный признак плоскоклеточного рака лёгкого (LUSC): амплификация CCND1 — одно из наиболее частых проявлений при LUSC. Амплификация SOX2 (3q26) — определяющий фактор плоскоклеточного рака лёгкого, редко встречающийся при аденокарциноме. Амплификация TP63, опять же, высокоспецифична для плоскоклеточных опухолей. Коамплификация PIK3CA, FGFR1, EGFR, MYC соответствует этому профилю. Широкий набор амплифицированных генов (многие регуляторы плоскоклеточной дифференцировки и сигнальных путей RTK/RAS/PI3K) отражает типичную биологию LUSC, определяемую числом копий. В отличие от этого, в аденокарциномах лёгких обычно наблюдаются точечные мутации/слияния генов KRAS, EGFR, ALK, ROS1, экзона 14 гена MET, ERBB2, а также меньше распространённых CNA плоскоклеточного типа. Ответ: Плоскоклеточный рак лёгких (LUSC).

Однако генерация этого вывода для одного образца занимает больше времени, чем классификация всего набора данных с помощью нашей модели, и на классификацию всего нашего набора данных уйдет около ~200 долларов США в виде платы за API.

Источник: towardsdatascience.com

✅ Найденные теги: Как, новости

ОСТАВЬТЕ СВОЙ КОММЕНТАРИЙ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

Астрономы будут получать оповещения о небесных явлениях в течение нескольких минут после их обнаружения. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной…

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых