Вишал Сингх

Содержание:

  1. Введение
  2. Деловая проблема
  3. Постановка задачи

А) Задание 1 (Классификация Царств)

1.1) EDA (исследовательский анализ данных)

1.2) Разработка функций

1.3) Визуализация данных

1.4) Обработка дисбаланса

1.5) Стандартизация

1.6) Моделирование

1.7) Важность и выбор функции

1.8) Пользовательские модели

Б) Задание 2 (Классификация типов ДНК)

1.1) EDA (исследовательский анализ данных)

1.2) Разработка функций

1.3) Визуализация данных

1.4) Работа с дисбалансом и стандартизация

1.5) Моделирование и важность функций

4 Развертывание

5. Вывод

6. Будущая работа

7 ресурсов

Все живые существа на земле состоят из тех или иных аминокислот. Эти аминокислоты содержат очень точную информацию о развитии и внутреннем метаболизме организма. Биологи всего мира обнаружили, что вся эта информация может быть закодирована с помощью некоторых азотистых оснований, присоединенных к аминокислотам. В этом докладе мы сосредоточимся на информации, полученной с помощью последовательности РНК. Поскольку РНК может содержать только азотистые основания A, G, C, T, информация о любой отдельной аминокислоте может быть закодирована последовательностью из 3 азотистых оснований (такое кодирование информации называется кодоном). Итак, здесь у нас есть 4 азотистых основания, и у нас есть 3 места, которые нужно заполнить этими аминооснованиями. Таким образом, всего может быть 64 кодона (4**3). В этой задаче я использовал эту информацию, чтобы сделать некоторые выводы об организме.

Бизнес-проблема:

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

Постановка задачи:

Имея данные об использовании кодонов, мы можем обучать модели машинного обучения, которые могли бы классифицировать организм по различным классам Царства и типа ДНК, если известны данные об использовании кодонов.

Бизнес-ограничения:

  • Не требуется низкая задержка
  • Требуется интерпретируемость
  • Требуется оценка вероятности существования организмов, принадлежащих к определенному классу.

Набор данных:

Данные были загружены репозиторием машинного обучения UCI (здесь).

Столбцы:

  • Колонка 1: Королевство
  • Столбец 2: тип ДНК
  • Столбцы 3: Идентификатор вида
  • Колонка 4 Nкодоны
  • Колонка 5 Название вида
  • Столбцы 6–69: данные об использовании кодонов для всех 64 кодонов.

ЗАДАНИЕ 1: Классификация царств

1.1 EDA (исследовательский анализ данных)

* Анализ целевых переменных (Королевство):

Итак, здесь у нас всего 12 классов, и наш набор данных сильно несбалансирован. В этой задаче мы пытаемся классифицировать все организмы на 3 класса (Археи, Бактерии, Эукариоты). Эти классы были определены на основе происхождения организмов. Итак, для этого я добавляю разные классы, чтобы сформировать эти три класса { Archaea : [arc], Bacteria: [bct, phg, plm], Eukaryotic: [pln, inv, vrt, mam, rod, pri]}. После разделения данные в 3 категории в соответствии с нашим сопоставлением, обратите внимание, что он отбросил класс vrl bzz, он не может быть сгруппирован ни в одну из трех категорий.

* Анализ функций:

  • NCodons (Количество кодонов)

Если мы посмотрим на коробчатую диаграмму распределения классов Царства, несмотря на то, что в классе архей слишком много точек данных, он имеет более широкое распределение, чем все другие классы. Блочная диаграмма показывает, что Ncodon является одной из важных особенностей.

  • Анализ особенностей Кондона:

После анализа всех кодонов с использованием boxplot PDF и CDF было обнаружено, что такие функции, как CUG, UGA, AGG, AAA, CGA, UGA, GGC, AUA, UCU, CUG, CUA, по-видимому, являются одними из важных функций для нашей задачи классификации.

Подробный исследовательский анализ данных выполнен в моей записной книжке на GitHub [нажмите здесь].

Подробный исследовательский анализ данных выполнен в моей записной книжке на GitHub [нажмите здесь].

1.2 Разработка функций

В этой задаче я попробовал два типа методов проектирования признаков: A) проектирование признаков на основе статистики B) некоторое проектирование признаков, основанное на знаниях предметной области.

A) Разработка функций на основе статистики:

Поскольку у нас есть 64 кодона, где значение каждого кодона представляет собой вероятность использования конкретного кодона, я решил добавить функции, которые могут определять изменчивость, центральную тенденцию, такую ​​как статистические переменные для каждой точки данных, и дополнительные функции, такие как эксцесс, среднее значение, мода, медиана, минимум, максимум, дисперсия, стандартное отклонение, 1-й квантиль, 2-й квантиль, 3-й квантиль. Эти параметры были рассчитаны для всех точек данных и добавлены в качестве признаков.

B) Функция, основанная на вхождении или предметной области.

Поскольку каждый кодон состоит из 3 разных наборов азотистых оснований, я также решил добавить функции, которые могли бы представлять появление каждого азотистого основания в виде комбинации униграммы и биграммы. Для этого я снова добавил функции, представляющие единичное появление нуклеиновых оснований (для этого для каждой точки данных я добавил вероятность того, что все кодоны, в названии которых есть U, увидят появление U. Аналогичную работу я проделал для A, G, C, U и назвали эти функции как sum_a, sum_g, sum_c, sum_u)

Как и в случае с униграммой, я также выполнил вхождение данных, просуммировав все столбцы, которые содержат двойное вхождение одной аминокислоты в ее названии. Например, для вычисления биграммных вхождений U я должен добавить вероятность появления UUA, UUG, UUC, AUU, GUU, CUU, UAU, UGU, UCU, я воспроизвел это также для G, C и A и добавил как функция в наборе данных.

Наконец, после объединения всех функций у меня получилось 84 функции. Позже я провел одномерный и двумерный анализ спроектированных функций, чтобы увидеть их релевантность. Результаты были оценены. Некоторые функции были намного лучше кодонов.

1.3 Визуализация данных

Для визуализации набора данных я пробовал и PCA, и TSNE. Подход PCA, похоже, не кластеризовал точки данных, но TSNE успешно кластеризовал точки данных для визуализации. Результаты TSNE показаны ниже.

1.3 Обработка дисбаланса данных.

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

1.4 Стандартизация данных

Кодоны - это вероятности появления кодонов, поэтому их значения находятся только между 0 и 1, но у нас есть некоторые функции, такие как NCodons, которые варьировались от 1000 до 25000, а также некоторые из инженерных функций были высокими. Поэтому я решил масштабировать функции в определенном диапазоне для повышения производительности. Для этого я провел эксперименты с двумя типами масштабирования MinMaxScaler и StandardScaler.

1.5 Моделирование

Для задачи классификации королевства я пробовал использовать все модели, начиная от линейных моделей, таких как (логистическая регрессия SVC (линейная)) и заканчивая нелинейными моделями, такими как наивный байесовский классификатор и классификатор KNN, деревья решений, случайный лес, XgBoost, LightGBM и т. д. Наконец, после экспериментирование с различными типами моделей наряду с различными методами передискретизации и стандартизации. SVC имеет тенденцию работать лучше с передискретизацией SMOTE и методом стандартизации StandardScaler, что дает показатель f1 0,952.

После настройки гиперпараметров было обнаружено, что SVC работает лучше при C = 1000.

1.6 Особенности Важность и выбор

Я также использовал методы выбора функций на основе важности функций моделей SVC. Поскольку SVC не выводил важность функции, мы попытались переставить важность с помощью SKlearn и использовали эту важность функции для задачи выбора функции. Я удалил функцию с отрицательной важностью, и это увеличило оценку F1 модели с 95,19 до 95,34.

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

1.7 Пользовательские модели

Я также пытался создавать собственные ансамблевые модели, используя SVC, наивную байесовскую модель, логистическую регрессию и другие ансамблевые модели. Но производительность не могла превзойти единственную производительность модели SVC от sklearn. Задание 2 Классификация типов ДНК

2.1 EDA (исследовательский анализ данных)

  • Анализ целевых переменных (тип ДНК):

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

* Анализ функций:

  • NCodons (Количество кодонов)

График Nкодонов для разных типов ДНК 1, 2 и 3 показывает, что для ДНК типа 0 есть некоторые выбросы, в то время как для типов ДНК 1 и 2 выбросов нет.

  • Другие функции кодона

Поскольку кодонов 64, мы не можем визуализировать их по одному, так как это занимает много времени. Поэтому лучше построить их все вместе, а затем визуализировать только выбранные объекты.

Теперь из приведенной выше диаграммы всех функций становится ясно, что такие функции, как UGA, CGA, CAU, UAU, UGG, GCC, AGG, GAU, CUA, AAU, могут содержать очень полезную информацию для классификации данные указывают на тип ДНК. Теперь давайте визуализируем эти функции, чтобы взглянуть на них.

Из графиков UGA видно, что он может отделить большинство точек данных ДНК типа 1 от ДНК типов 0 и 2. Точно так же признаки CAU и UAU могут классифицировать точки данных ДНК типа 2 от типов ДНК 0 и 1, тогда как признаки CGA, похоже, почти разделяет точки данных всех классов на своей ящичной диаграмме.

2.2 Разработка функций:

Для этой части я решил извлечь те же признаки, что и для задачи классификации Царств (эту вещь мы хорошо обсуждали в части 1). Итак, выполнив часть извлечения функций, давайте попробуем визуализировать эти инженерные функции и проверить, как они могут быть полезны для нашей задачи.

Такие функции, как sum_c, q3 и sum_a на блочной диаграмме, похоже, классифицируют один класс с другим. Кроме того, двумерные графики признаков показывают, что распределение классов меньше перекрывается, если мы принимаем во внимание эти признаки. Из приведенных выше графиков очень ясно, что мы разработали несколько очень хороших функций, которые могут быть очень полезны для задач классификации. [Полный EDA см. Блокнот здесь]

2.3 Визуализация данных

Результаты визуализации данных в меньшее скрытое пространство в этой задаче такие же, как и в предыдущей задаче. PCA не удалось сгруппировать эти данные в меньшие скрытые пространства, в то время как TSNE проделал хорошую работу и разбил точки данных на небольшие кластеры.

2.3 Обработка дисбаланса данных и стандартизация данных

Для обработки дисбаланса данных я использовал тот же подход и использовал SMOTE и ADAsyn для передискретизации.

Поскольку наши данные имеют особенности разных масштабов, возникает необходимость стандартизации данных. Для задачи стандартизации данных MinMaxScaler и StandardScaler.

1.4 Моделирование

Для экспериментов с различными методами масштабирования и передискретизации я создал разные наборы данных, как показано ниже.

Что касается моделирования, я пробовал использовать все модели, такие как логистическая регрессия, наивный байесовский классификатор, классификатор опорных векторов, деревья решений и другие модели на основе ансамбля, такие как Random Forest, Xgboost и LightGBM. Из всех моделей LightGBM работает лучше с показателем f1 0,9956.

The top 15 most pos. important feature are:  ['UGA', 'kurt', 'CUA', 'GAU', 'GAC', 'sum_u', 'GAG', 'UGG', 'CAG', 'AAG', 'CGU', 'GAA', 'sum_g', 'AUU', 'GGC']

Есть 3 разработанные функции, которые входят в число 15 наиболее важных функций, демонстрирующих преимущества разработки функций,

Как и в задаче 1 по классификации королевств, в этой задаче я также пытался объединить логистическую регрессию, KNN, наивную байесовскую модель и другие модели, но результаты не смогли превзойти показатель f1 модели LightGBM.

Развертывание:

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

def make_prediction_kingdom(X, clf_path, class_encoding_path, std_path, good_features_path ):   
    X = pd.DataFrame(X.values.reshape(1, -1), columns = X.index, dtype = float)
    
    #load class encoding
    with open(class_encoding_path, 'rb') as file:
        le = pickle.load(file)
    file.close()
    
    # load std_path file
    with open(std_path, 'rb') as file:
        std_ = pickle.load(file)
    file.close()
    
    # load classifier     
    with open(clf_path, 'rb') as file:
        clf = pickle.load(file)
    file.close()
with open(good_features_path, 'rb') as file:
        good_features = pickle.load(file)
    file.close()
    
    
    # function for calculating double feature
    def get_XX_feature(val, f):
        temp = 0
        for col in val.columns:
            if col[0:2]==f or col[-1:-3]==f or (col[0]==f[0] and col[-1]==f[0]):
                try: 
                    temp = temp + float(val.iloc[0][col])
                except:
                    print(f'There has been a error while calculating {f}')
        return temp
    
    # function for calculating single occurance feature
    def get_X_feature(val, f):
        temp = 0
        for col in val.columns:
            if f in col and len(col)==3:
                try:
                    temp = temp + float(val.iloc[0][col])
                except:
                    print(f'There has been a error while calculating {f}')
        return temp
    X_copy = X.copy()
    kurt = X.kurtosis(axis = 1).values[0]
    med = X.median(axis = 1).values[0]
    mode = X.mode(axis = 1).values[0][0]
    var = X.var(axis = 1).values[0]
    max_ = X.max(axis = 1).values[0]
    min_ = X.min(axis = 1).values[0]
    q1 = X.quantile(0.25, axis = 1).values[0]
    q2 = X.quantile(0.50, axis = 1).values[0]
    q3 = X.quantile(0.75, axis = 1).values[0]
    std = X.std(axis = 1).values[0]
    sum_ = X.sum(axis = 1).values[0]
    UU = get_XX_feature(X, 'UU')
    AA = get_XX_feature(X, 'AA')
    CC = get_XX_feature(X, 'CC')
    GG = get_XX_feature(X, 'GG')
    sum_g = get_X_feature(X, 'G')
    sum_a = get_X_feature(X, 'A')
    sum_c = get_X_feature(X, 'C')
    sum_u = get_X_feature(X, 'U')
    
    X['kurt'] = kurt
    X['median'] = med
    X['mode'] = mode
    X['var'] = var
    X['max'] = max_
    X['min'] = min_
    X['q1'] = q1
    X['q2'] = q2
    X['q3'] = q3
    X['std'] = std
    X['sum'] = sum_
    X['UU'] = UU
    X['AA'] = AA
    X['CC'] = CC
    X['GG'] = GG
    X['sum_g'] = sum_g
    X['sum_a'] =sum_a
    X['sum_c'] = sum_c
    X['sum_u'] = sum_u
    
    X = pd.DataFrame(std_.transform(X, ), columns = X.columns)
    
    X = X[good_features]
    pred = clf.predict(X)
    return pred, X.values.tolist()[0]

Подобно коду классификации королевств, я также создал функцию для получения исходных данных и прогнозирования типов ДНК.

def make_prediction_dnatype(X, clf_path ):   
    X = pd.DataFrame(X.values.reshape(1, -1), columns = X.index, dtype = float)
    
    # load classifier     
    with open(clf_path, 'rb') as file:
        clf = pickle.load(file)
    file.close()
    
    
    # function for calculating double feature
    def get_XX_feature(X, f):
        temp = 0
        for col in X.columns:
            if col[0:2]==f or col[-1:-3]==f or (col[0]==f[0] and col[-1]==f[0]):
                try: 
                    temp = temp + float(X.iloc[0][col])
                except:
                    print(f'There has been a error while calculating {f}')
        return temp
    
    # function for calculating single occurance feature
    def get_X_feature(X, f):
        temp = 0
        for col in X.columns:
            if f in col and len(col)==3:
                try:
                    temp = temp + float(X.iloc[0][col])
                except:
                    print(f'There has been a error while calculating {f}')
        return temp
    X_copy = X.copy()
    
    X['kurt'] = X_copy.kurtosis(axis = 1).values[0]
    X['median'] = X_copy.median(axis = 1).values[0]
    X['mode'] = X_copy.mode(axis = 1).values[0][0]
    X['var'] = X_copy.var(axis = 1).values[0]
    X['min'] = X_copy.min(axis = 1).values[0]
    X['max'] = X_copy.min(axis = 1).values[0]
    X['q1'] = X_copy.quantile(0.25, axis = 1).values[0]
    X['q2'] = X_copy.quantile(0.50, axis = 1).values[0]
    X['q3'] = X_copy.quantile(0.75, axis = 1).values[0]
    X['std'] = X_copy.std(axis = 1).values[0]
    X['sum'] = X_copy.sum(axis = 1).values[0]
    X['UU'] = get_XX_feature(X_copy, 'UU')
    X['AA'] = get_XX_feature(X_copy, 'AA')
    X['CC'] = get_XX_feature(X_copy, 'CC')
    X['GG'] = get_XX_feature(X_copy, 'GG')
    X['sum_g'] = get_X_feature(X_copy, 'G')
    X['sum_a'] = get_X_feature(X_copy, 'A')
    X['sum_c'] = get_X_feature(X_copy, 'C')
    X['sum_u'] = get_X_feature(X_copy, 'U')
    X = X[clf.feature_name_]
    pred = clf.predict(X)
    pred_proba  = clf.predict_proba(X)
    return pred

Я сохранил обе вышеуказанные функции в utils.py. Вот моя структура каталогов для развертывания приложения Flask.

Во-первых, давайте создадим файл index.html, который будет шаблоном для нашего приложения. В шаблоне я создал систему загрузки файлов для загрузки файла .txt, разделенного запятыми, в качестве входных данных в фиксированном формате. Вот мой код для файла .html.

<!doctype html>
<html lang="en">
 <body>
<form class="form_mine" action="{{url_for('submit')}}" method="post"        enctype="multipart/form-data">
   <input type="file" id="upload" name="upload"  hidden/>
   <label for="upload" class="btn btn-secondary">Choose file</label>
   <input type="submit" class="btn btn-primary">
</form>

 <p class="output_text">{{ prediction_text }}</p>
  
 </div>
  </body>
</html>

Теперь, после прогнозирования функций для обеих задач, давайте напишем код для нашего приложения app.py для фляги. Для этого сначала мы создаем маршрут к index.html, а затем создаем маршрут для прогнозирования модели с использованием ранее определенных функций.

from flask import Flask, render_template, request
import numpy as np
import pickle
import pandas as pd
import os
from utils import make_prediction_kingdom, make_prediction_dnatype
from flask import Flask, render_template, request
from werkzeug.utils import secure_filename
# columns in raw input data 
columns['Ncodons','UUU','UUC','UUA','UUG','CUU','CUC','CUA','CUG','AUU','AUC','AUA','AUG','GUU','GUC','GUA','GUG','GCU','GCC','GCA','GCG','CCU','CCC','CCA','CCG','UGG','GGU','GGC','GGA','GGG','UCU', 'UCC','UCA','UCG','AGU','AGC','ACU','ACC','ACA','ACG','UAU','UAC','CAA','CAG','AAU','AAC','UGU','UGC','CAU','CAC','AAA','AAG','CGU','CGC','CGA','CGG','AGA','AGG','GAU','GAC','GAA','GAG','UAA','UAG','UGA']
# labels for kingdom and dnatype
kingdom_classes = ['Archea', 'Bacteria', 'Eukaryots']
DNAtype_classes = ['genomic', 'mitochondrial', 'chloroplast']
# initializing our flask app
app=Flask(__name__,template_folder='templetes', static_folder = 'img')
app.config['UPLOAD_FOLDER'] = 'img'
add route to index.html
@app.route("/")
def index():
    return render_template('index.html')
#function for result prediction
@app.route('/submit', methods=['POST'])
def submit():
    if request.method == 'POST':
        file = request.files['upload']
        if file.filename=="":
            return render_template('index.html', text = 'Please choose a file to upload')
        filename = secure_filename(file.filename)
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
        with open(os.path.join(app.config['UPLOAD_FOLDER'], filename),"r") as f:
            con = f.readlines()
        text = ''.join(con)
        input_val = list(map(float, text.split(',')))
        data = pd.Series(input_val, columns)
        pred_kingdom, d = make_prediction_kingdom(data, 'kingdom/clf_kingdom_svm.pkl' , 'kingdom/class_encoding_kingdom.pkl', 'kingdom/std_kingdom.pkl', 'kingdom/good_features_kingdom.pkl')
        pred_dnatype = make_prediction_dnatype(data, clf_path = 'DNAtype/clf_best_dnatype.pkl' )
        prediction_text = f'The following organism belong to {kingdom_classes[pred_kingdom[0]]} Kingdom and its DNAtype is {DNAtype_classes[pred_dnatype[0]]}'
        os.remove(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    return render_template('index.html', prediction_text = prediction_text)
if __name__ == '__main__':
    app.run(debug=True)
    # app.run(debug=True)

На данный момент наше приложение Flask готово к прогнозированию, и мы можем запустить его с помощью следующей команды. Он будет работать на локальном хосте: 5000.

python app.py
  • Создание файла Docker

Поскольку мы закончили с запуском приложения на локальном хосте, давайте начнем с докеризации нашего приложения с помощью контейнера докеров. Для докеризации нашего приложения я буду использовать последние континуум-образы anaconda из docker-hub. Вот содержимое файла докера. Я также сделал файл requirements.txt и перечислил все библиотеки, которые мы используем.

From continuumio/anaconda3:latest
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD [ "python3", "app.py"]

Перед развертыванием нашего приложения в докере нам нужно внести одно изменение в наш файл app.py, так как мы должны добавить порт и хост, на котором наш скрипт будет запускаться внутри контейнера. Поскольку мы хотим сделать его доступным за пределами нашего образа, мы устанавливаем хост как 0.0.0.0 и порт как 5000 в нашем файле app.py.

if __name__ == '__main__':
    port = os.environ.get("PORT", 5000)
    app.run(debug=True, host='0.0.0.0', port = port)

После того, как мы готовы с файлом докера, нам нужна команда сборки докера, чтобы создать контейнер докера, а затем мы запускаем контейнер докера.

docker build -f [path-to-docker-file] -t codonusages:latest .
docker run -p 5000:5000 colorme

Демо: http://codonusage.herokuapp.com/

Заключение :

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

Будущая работа :

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

Ресурсы :

  1. Набор данных: https://archive.ics.uci.edu/ml/datasets/Codon+usage
  2. Репозиторий Github для проекта: https://github.com/vishalsingha/Kingdom-and-DNAtype-Classification-using-Codon-Bias-Level
  3. Документ: https://www.biorxiv.org/content/10.1101/2020.10.26.356295v1.full.pdf

4. Прикладные курсы ИИ (AAIC)