Skip to content

Latest commit

 

History

History
418 lines (290 loc) · 38 KB

lecture.md

File metadata and controls

418 lines (290 loc) · 38 KB

Первая лекция по ML

Зачем нужно машинное обучение?

Первые попытки системного анализа данных берут свое начало еще с разработки в 1794г. Карлом Гауссом метода наименьших квадратов. Задача восстанавления зависимости, которую он решал этим методом стоит перед нами и по сей день, но она является одной из многих, которую сегодня можно решить методами машинного обучения.

Первые интеллектуальные системы были экспертными, т.е. это были системы, обеспечивающие выполнение набора эвристических правил, описанных разработчиками (в духе конструкций if-else), подобно тому, как это делают реальные эксперты-люди. Подобные экспертные системы до сих пор находят свое применение в областях, где правила могут быть сформулированы четко, а зависимости ясны из природы наблюдаемого процесса. Именно эти ограничения и не позволяют разрабатывать гибкие решения.

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

Именно подходы машинного обучения позволяют перейти от попыток формулирования эвристических правил к поиску зависимостей в данных и построению эффективных и гибких моделей для решения задачи.

Задачи решаемые при помощи машинного обучения

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

Модели, которые учатся на парах объект-ответ, реализуют алгоритм обучения с учителем, так как "учитель" показывает модели ответы в каждом наблюдениии.

Определение почтового индекса по рукописным текстам на конверте. Здесь объектом будет сканированное изображение почерка человека, а ответ - фактические цифры почтового индекса. Чтобы создать набор данных, необходимо отсканировать конверты, выделить текст, распознать и создать таблицу объект-ответ, где объектами являются отсканированные изображения рукописного текста, а ответами - распознанные цифры.

Распознование лиц. Здесь объектом будет фото человека, а ответом его ФИО. Чтобы создать набор данных, необходимо создать таблицу где объектом является фото человека, а ответом - его ФИО.

Алгоритм обучения без учителя или неконтролируемого обучения - это вид алгоритма обучения, где даны только объекты, а ответы - нет. Есть много успешных сфер применения этого алгоритма, но как правило подобные модели обучения сложнее интерпретировать и оценить.

Определение тем в наборе постов. Есть большое количество постов и их надо как-то разбить на категории. У вас нет предварительной ифнормации о том, какие темы там затрагиваются и сколько их.

Сегментирование клиентов на группы с похожими предпочтениями Имея набор записей о клиентах, вы можете определить группы клиентов со схожими предпочтениями. Для торгового сайта такими группами могут быть «родители», «книгочеи» или «геймеры». Поскольку вы не знаете заранее о существовании этих групп и их количестве, у вас нет ответов.

В машинном обучении каждый объект или строка называются примером (sample) или точкой данных (data point), а столбцы-свойства, которые описывают эти примеры, называются характеристиками или признаками (features).

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

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

  1. На какой вопрос(ы) я пытаюсь ответить? Собранные данные могут ответить на этот вопрос?
  2. Как лучше всего сформулировать свой вопрос(ы) с точки зрения задач машинного обучения?
  3. У меня собрано достаточно данных, чтобы составить представление о задаче, которую я хочу решить?
  4. Какие признаки я извлек и помогут ли они мне получить правильные прогнозы?
  5. Как я буду измерять эффективность решения задачи?
  6. Как решение, полученное с помощью машинного обучения, будет взаимодействовать с другими компонентами моего исследования или бизнес-продукта?

А что .... python?

Python стал общепринятым языком для большого количества сфер науки и разработки. Он сочетает несколько парадигм программирования, но в его основе лежит ООП, что делает его очень гибким и удобным инструментом. И его синтаксис невероятно сексуален.

scikit-learn - главный плюс, содержит большое количество алгоритмов машшиного обучения и документацию по каждому из них. Она широко используется в промышленности и науке, а в Интернете имеется богатый выбор обучающих материалов и примеров программного кода. scikit-learn прекрасно работает с рядом других научных инструментов Python.

pip install numpy scipy matplotlib ipython scikit-learn pandas mglearn

Основные библиотеки

Jupyter Notebook

Jupyter Notebook представляет собой интерактивную среду для запуска программного кода в браузере. Это отличный инструмент для разведочного анализа данных и широко используется специалистами по анализу данных.

NumPy

NumPy – это один из основных пакетов для научных вычислений в Python. Он содержит функциональные возможности для работы с многомерными массивами, высокоуровневыми математическими функциями (операции линейной алгебры, преобразование Фурье, генератор псевдослучайных чисел).

#Example
import numpy as np 
x = np.array([1,2,3],[4,5,6])
print("x:\n{}".format(x))
"""
x:
[[1 2 3]
[4 5 6]]
"""

SciPy

SciPy – это набор функций для научных вычислений в Python. Помимо всего прочего, он предлагает продвинутые процедуры линейной алгебры, математическую оптимизацию функций, обработку сигналов, специальные математические функции и статистические функции.

from scipy import sparse
# Создаем 2D массив NumPy с единицами по главной диагонали и нулями в остальных ячейках
eye = np.eye(4)
print("массив NumPy:\n{}".format(eye))
"""
массив NumPy: 
[[ 1. 0. 0. 0.] 
[ 0. 1. 0. 0.]
[ 0. 0. 1. 0.]
[ 0. 0. 0. 1.]]
"""

# Преобразовываем массив NumPy в разреженную матрицу SciPy в формате CSR
# Сохраняем лишь ненулевые элементы
sparse_matrix = sparse.csr_matrix(eye)
print("\nразреженная матрица SciPy в формате CSR:\n{}".format(sparse_matrix))
"""
разреженная матрица SciPy в формате CSR: 
(0, 0) 1.0
(1, 1) 1.0
(2, 2) 1.0
(3, 3) 1.0
"""

matplotlib

matplotlib – это основная библиотека для построения научных графиков в Python. Она включает функции для создания высококачественных визуализаций типа линейных диаграмм, гистограмм, диаграмм разброса и т.д. Визуализация данных и различных аспектов вашего анализа может дать вам важную информацию, и мы будем использовать matplotlib для всех наших визуализаций.

%matplotlib inline
import matplotlib.pyplot as plt
# Генерируем последовательность чисел от -10 до 10 с 100 шагами
x = np.linspace(-10, 10, 100)
# Создаем второй массив с помощью синуса
y = np.sin(x)
# Функция создает линейный график на основе двух массивов 
plt.plot(x, y, marker="x")

pandas

pandas – библиотека Python для обработки и анализа данных. Она построена на основе структуры данных, называемой DataFrame и смоделированной по принципу датафреймов среды статистического программирования R. Проще говоря, DataFrame библиотеки pandas представляет собой таблицу, похожую на электронную таблицу Excel.

import pandas as pd
# создаем простой набор данных с характеристиками пользователей
data = {'Name': ["John", "Anna", "Peter", "Linda"],
'Location' : ["New York", "Paris", "Berlin", "London"], 'Age' : [24, 13, 53, 33]
}
data_pandas = pd.DataFrame(data)
# IPython.display позволяет "красиво напечатать" датафреймы 
# в Jupyter notebook
display(data_pandas)
# Выбрать все строки, в которых значение столбца age больше 30
display(data_pandas[data_pandas.Age > 30])

Уснул читая это дерьмо? Теперь будет РЕАЛЬНЫЙ МЛ

Предположим, что вы внезапно решили стать ботаником, и решили классифицировать сорта ирисов, которые вы собрали. Измерили некоторые характеристики ирисов:

  1. Длину и ширину лепестков
  2. Длину и шиирну чашелистиков

Кроме того, вы не первый, кто в роду увлекся такой редкостной фигней. Ваши родственники уже составили экспертное мнение по поводу сортов ирисов и отнесли их к сортам setosa, versicolor и virginica.

Ирис

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

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

Загрузка данных

Данные которые мы будем загружать - это набор Iris, это классический набор данных в машинном обучении и статистике. Он включен в модуль dataset.

from sklearn.datasets import load_iris
iris_dataset = load_iris()

Объект iris, возвращаемый load_iris, является объектом Bunch, который очень похож на словарь. Он содержит ключи и значения:

print("Ключи iris_dataset: \n{}".format(iris_dataset.keys()))
Ключи iris_dataset:
dict_keys(['target_names', 'feature_names', 'DESCR', 'data', 'target'])

Значение ключа DESCR – это краткое описание набора данных

print(iris_dataset['DESCR'][:193] + "\n...")

Значение ключа target_names – это массив строк, содержащий сорта цветов, которые мы хотим предсказать:

print("Названия ответов: {}".format(iris_dataset['target_names']))

Значение feature_names – это список строк с описанием каждого признака:

print("Название празинаков:\n{}".format(iris_dataset['feature_names']))

Данные записаны в масивах target и data - NumPy массив, который содержит количественные измерения длины чашелистиков, ширины чашелистиков, длины лепестков и ширины лепестков. Посмотреть это можно через type

print("Тип массива data: {}".format(type(iris_dataset['data'])))

Строки в массиве data соответствуют цветам ириса, а столбцы представляют собой четыре признака, которые были измерены для каждого цветка:

print("Форма массива data: {}".format(iris_dataset['data'].shape))

Видно, что массив содердит 150 различных цветов с 4 признаками. В машинном обучения подразделяют sample (примеры) - элементы обучающей выбоорки, feature (признаки) - свойства или характеристики. shape (форма) - матрица данных определяется количеством примеров * на количество признаков. В библиотеке scikit-learn данные всегда будут предоставлены в такой форме.

Выведем первые пять строк из обучающей выборки:

print("Первые пять строк массива data:\n{}".format(iris_dataset['data'][:5]))

Посмотрев на эти данные, видно, что пять цветов имеют ширину лепестка 0.2 и первый цветок имеет самую большую длину чашелистика 5.1 см.

Массив target содержит сорта уже измеренных цветов, тоже записанные в виде NumPy массива.

print("Форма массива target: {}".format(iris_dataset['target'].shape))

Сорта кодируются как целые числа от 0 до 2:

print("Ответы :\n{}".format(iris_dataset['target']))

Значение чисел в миссиве target: 0 - setosa 1 - versicolor 2 - virginica.

Обучающие данные и тестовые данные

Но основе данных нам необходимо создать модель машинного обучения, которая будет подсказывать нам сорта ириса исходя из их характеристик (feature). Но для увернности ее использования, ее необходимо протестировать и понять, насколько хорошо наша модель подходит для данного наборв данных.

Для оченки качества модели мы не можем использовать данные, которые были взяты нами для построения модели. Почему же? Потому что наша модель просто запомнит все вариции фичей и будет работать только на них, а не сможет обобщать данные.

Чтобы оценить качество модели, мы предоставим ей новые размеченные данные (данные которые модель ранее не видела). Обычно это делается путем разбиения данных на две части. Одну часть данных мы будем использовать как набор для обучения, она называется обучающими данными (training data), а вторую часть данных - для проверки качества модели, она называется тестовыми данными (test data), тестовым набором (test set) или контрольным набором (hold-out set).

В библиотеке scikit-learn есть функция train_test_split, которая перемешивает набор данных и разбивает его на две части. Эта функция отбирает в обучающий набор 75% строк данных с соответствующими метками. Оставшиеся 25% данных с метками объявляются тестовым набором. Вопрос о том, сколько данных отбирать в обучающий и тестовый наборы, является дискуссионным, однако использование тестового набора, содержащего 25% данных, является хорошим правилом.

В машином обучении данные, как правило, обозначаются заглавной Х, тогда как метки обозночаются строчной y. Это из стандартного вида функции f(x)=y, где x аргумент функции, а y - её значение. В соответсвии с правилами представления математических объектов, мы используем X, потому что данные представляют собой двумерный массив (матрицу), и строчную у - одномерный массив.

from sklearn.model_selection import train_test_split 
	X_train, 	X_test, y_train, y_test = train_test_split(
	iris_dataset['data'], iris_dataset['target'], random_state=0)

Перед разбиением фукнция train_test_split, перемешивает набор данных с помошью генератора случайных чиел. Если мы возьмем последние 25% обучающей выборки, то данные будут иметь метку 2, так как массив отсротирован по меткам. Чтобы в точности повторно воспроизвести выборки, мы будем использовать генератор псевдослучайных чисел с фиксированным стартовым значением random_state, что позволит сделать результат воспроизводимым, и набор данных будет генерироваться всегда один и тот же.

print("Форма массива X_train: {}".format(X_train.shape))
print("Форма массива y_train: {}".format(y_train.shape))
print("Форма массива  X_test: {}".format(X_test.shape))
print("Форма массива  y_test: {}".format(y_test.shape))

Посмотрим на данные.

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

Один из лучших способов исследовать данные - визуализировать их. Это можно сделать, используя диаграмму рассеяния. В диаграмме рассеяния один признак откладывается по оси х, а другой признак по оси у; каждому наблюдению соответствует точка. На одном дисплее компьютера можно разместить 2 -3 признака одновренно. Для показа на экране более 3 признаком можно использовать диаграммы рассеяния (scatterplot matrix) или парные диаграммы рассеяния (pair plots), на которых будут изображены все возможные пары признаков. Однако, вы должны помнить, что матрица диаграмм рассеяния не показывает взаимодействие между всеми признаками сразу, поэтому некоторые интересные аспекты данных не будут выявлены с помощью этих графиков.

Точки данных окрашены в соответствии с сортами ириса, к которым они относятся. Чтобы построить диаграммы, мы сначала преобразовываем массив NumPy в DataFrame (основный тип данных в библиотеке pandas). В pandas есть функция для создания парных диаграмм рассеяния под названием scatter_matrix.

# создаем dataframe из данных в массиве X_train
# маркируем столбцы, используя строки в iris_dataset.feature_names
iris_dataframe = pd.DataFrame(X_train, columns=iris_dataset.feature_names)
# создаем матриуц рессеяния из dataframe, цвет точек задается с помощью y_train
 grr = pd.scatter_matrix(iris_dataframe, c=y_train, figsize=(15, 15), marker='o', hist_kwds={'bins': 20}, s=60, alpha=.8, cmap=mglearn.cm3)

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

Первая ваш модель: метод k ближайших соседей

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

В scikit-learn все модели машинного обучения реализованы в собственных классах, называемых классами Estimator. Алгоритм классификации на основе метода k ближайших соседей реализован в классификаторе KNeighborsClassifier модуля neighbors. Прежде чем использовать эту модель, нам нужно создать объект-экземпляр класса. Это произойдет, когда мы зададим параметры модели. Самым важным параметром KNeighborsClassifier является количество соседей, которые мы установим равным 1:

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)

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

Для построения модели на обучающем наборе, мы вызываем метод fit объекта knn, который принимает в качестве аргументов массив NumPy X_train, содержащий обучающие данные, и массив NumPy y_train, соответствующий обучающим меткам:

knn.fit(X_train, y_train)

Метод fit возвращает сам объект knn (и изменяет его), таким образом, мы получаем строковое представление нашего классификатора. Оно показывает нам, какие параметры были использованы при создании модели. Почти все параметры имеют значения по умолчанию, но вы также можете обнаружить параметр n_neighbor=1, заданный нами. Большинство моделей в scikit-learn имеют массу параметров, но большая часть из них связана с оптимизацией скорости вычислений или предназначена для особых случаев использования.

Прогнозируем, но не как Росгидрометцентр.

Теперь мы можем получить прогнозы, применив модель к новым данным, по которым мы еще не знаем результатов. Давайте представим, что вы опять решили заняться этим делом и сделали пару измерений. Получилось: длина чашелистика 5 см, ширина чашелистика 2.9 см, длина лепестка 1 см и ширина лепестка 0.2 см. К какому сорту ириса можно отнести этот цветок.

X_new = np.array([[5, 2.9, 1, 0.2]])
print("Форма массива X_new: {}".format(X_new.shape))

Обратите внимание, что мы записали измерения по одному цветку в двумерный массив NumPy, поскольку scikit-learn работает с двумерными массивами данных.

prediction = knn.predict(X_new) 
print("Прогноз: {}".format(prediction))
print("Спрогнозированная метка: {}".format(iris_dataset['target_names'][prediction]))

Наша модель предсказывает, что этот новый цветок ириса принадлежит к классу 0, что означает сорт setosa. Но как узнать, можем ли мы доверять нашей модели?

Качество модели

Настал тот момент когда из закрамов необходимо достать тестовый набор. Эти данные не использовались для построения модели, но мы знаем правильные сорта для каждого ириса в тестовом наборе. Таким вот образом мы можем сдлеать прогноз для каждого ириса в тестовом наборе и сравнить его с фактическим. Мы можем оценить качество модели, вычислив точность (accuracy) – процент цветов, для которых модель правильно спрогнозировала сорта:

y_pred = knn.predict(X_test)
print("Прогнозы для тестовго набора:\n {}".format(y_pred))
print("Правильность на тестовом наборе: {:.2f}".format(np.mean(y_pred == y_test)))

Кроме того, мы можем использовать метод score объекта knn, который вычисляет правильность модели для тестового набора:

print("Правильность на тестовом наборе: {:.2f}".format(knn.score(X_test, y_test)))

Точность этой модели для тестового набора составляет около 0.97, что означает, что мы дали правильный прогноз для 97% ирисов в тестовом наборе. При некоторых математических допущениях, это означает, что мы можем ожидать, что наша модель в 97% случаев даст правильный прогноз для новых ирисов. Для нашего ботаника-любителя этот высокий уровень правильности означает, что наша модель может быть достаточно надежной в использовании.

HomeWork

В библиотеке sklearn.datasets cуществует еще тестовые выборки.

  1. make_circles - создаст выборку из кругов
  2. make_classification - создаст случайную задачу классификации из n-класса.
  3. make_blobs - создаст изотропные гауссовы капли для кластеризации. Необходимо создать knn классификацию, и поиграть с параметрами создания обучающей выборки и как вилияет количество соседей на прогнозирование.
from sklearn.datasets import make_blobs
X,y = make_blobs(random_state=1,n_samples=100, n_features=2, centers=4)
#Взлянем на набор данных
plt.scatter(X[:, 0], X[:, 1], marker='o', c=y)
plt.show()
fig, axes = plt.subplots(1, 3, figsize = (10, 3))

for n_neighbors, ax in zip([1, 3, 9], axes):
    clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(X, y)
    mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
    ax.set_title("{} neighbor(s)".format(n_neighbors))
    ax.set_xlabel("feature 0")
    ax.set_ylabel("feature 1")
axes[0].legend(loc=3)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=2)

training_accuracy = []
test_accuracy = []
# Попробуй установить количество соседей (n_neighbors) от 1 до 10
neighbors_settings = range(1, 21)
for n_neighbors in neighbors_settings:
    # построить модель
    reg = KNeighborsClassifier(n_neighbors=n_neighbors)
    reg.fit(X_train, y_train)
    # запишем обучающую точность 
    training_accuracy.append(reg.score(X_train, y_train))
    # запишем тестовую точность 
    test_accuracy.append(reg.score(X_test, y_test))

plt.plot(neighbors_settings,
         training_accuracy, label="Обучающая точность ")
plt.plot(neighbors_settings,
        test_accuracy, label="Тестовая точность")
plt.ylabel("Точность")
plt.xlabel("n_соседей")
plt.legend()