Merge pull request 'kochkareva_elizaveta_lab_2 is ready' (#194) from kochkareva_elizaveta_lab_2 into main

Reviewed-on: http://student.git.athene.tech/Alexey/IIS_2023_1/pulls/194
This commit is contained in:
Alexey 2023-12-05 22:59:34 +04:00
commit e64084bb6e
2 changed files with 224 additions and 0 deletions

View File

@ -0,0 +1,136 @@
# Лабораторная работа 2. Вариант 15
### Задание
Выполнить ранжирование признаков с помощью указанных по
варианту моделей. Отобразить получившиеся значения\оценки каждого
признака каждым методом\моделью и среднюю оценку.
3 модели:
- Случайное Лассо (RandomizedLasso)
- Рекурсивное сокращение признаков (Recursive Feature Elimination RFE)
- Линейная корреляция (f_regression)
### Как запустить лабораторную работу
Для запуска программы необходимо с помощью командной строки в корневой директории файлов прокета прописать:
```
python main.py
```
### Какие технологии использовали
- Библиотека *numpy* для работы с массивами.
- Библиотека *itemgetter* для использования функции для выбора элементов из коллекции.
- Библиотека *sklearn*:
- *LinearRegression* для создания и работы с моделью Линейной регрессии.
- *Ridge* для создания и работы с моделью линейной регрессии с регуляризацией
- *MinMaxScaler* - для нормализации данных путем масштабирования значений признаков в диапазоне от 0 до 1.
- *RFE, f_regression* - RFE используется для рекурсивного отбора признаков, а f_regression - для вычисления корреляции между каждым признаком и целевой переменной.
### Описание лабораторной работы
#### Генерация данных
Создаем массив `X` размером (750, 14), где каждый элемент выбирается случайным образом из равномерного распределения на интервале от 0 до 1. Затем вычисляем массив `Y`, используя формулу, отрадающую зависимость значения `Y` от значений в массиве `X` и состоящую из случайного шума.
Изменяем значения последних четырех столбцов массива `X`. Значения этих столбцов становятся суммой первых четырех столбцов и случайного шума. Создаем список names, который содержит названия признаков, и пустой словарь ranks, который будет использоваться для хранения значений важности признаков.
```python
np.random.seed(0)
size = 750
X = np.random.uniform(0, 1, (size, 14))
# Задаем функцию-выход: регрессионную проблему Фридмана
Y = (10 * np.sin(np.pi*X[:, 0]*X[:, 1]) + 20*(X[:, 2] - .5)**2 + 10*X[:, 3] + 5*X[:, 4]**5 + np.random.normal(0, 1))
# Добавляем зависимость признаков
X[:, 10:] = X[:, :4] + np.random.normal(0, .025, (size, 4))
names = ["x%s" % i for i in range(1, 15)] # - список признаков вида ['x1', 'x2', 'x3', ..., 'x14']
ranks = dict()
```
#### Обработка результатов
Создаем функцию `rank_to_dict`, которая принимает два аргумента: `ranks` (оценки важности признаков) и `names` (названия признаков). В данной функции создается словарь, используя `zip` для объединения названий признаков и округленных оценок важности признаков. Названия признаков становятся ключами словаря, а округленные оценки - значениями.
```python
def rank_to_dict(ranks, names):
# получение абсолютных значений оценок(модуля)
ranks = np.abs(ranks)
minmax = MinMaxScaler()
# преобразование данных
ranks = minmax.fit_transform(np.array(ranks).reshape(14, 1)).ravel()
# округление элементов массива
ranks = map(lambda x: round(x, 2), ranks)
# преобразование данных
return dict(zip(names, ranks))
```
#### Модель Ridge
Так как по заданию необходимо работать с устаревшей моделью случайное *Лассо (RandomizedLasso)*, воспользуемся *Ridge-регрессией (Ridge Regression)*.
Создадим экземпляр модели *Ridge*, которая используется для выполнения регрессии с использованием линейной комбинации признаков, которая применяет регуляризацию L2 для уменьшения влияния мультиколлинеарности в данных.
Обучаем модель *Ridge* с использованием метода `.fit(X, Y)`. Здесь `X` представляет собой матрицу признаков (независимые переменные), а `Y` - вектор целевой переменной (зависимая переменная). Модель обучается на этих данных, чтобы найти оптимальные значения коэффициентов регрессии. Далее отправляем полученные коэффициенты в метод `rank_to_dict` для обрезования данных в необходимый вид.
```python
ridge_model = Ridge()
ridge_model.fit(X, Y)
ranks['Ridge'] = rank_to_dict(ridge_model.coef_, names)
```
#### Модель рекурсивное сокращение признаков (Recursive Feature Elimination RFE)
Для работы с моделью рекурсивного сокрщения признаков создадим две функции: `recursive_feature_elimination()` и `rank_to_dict_rfe(ranking, names)`.
В функции `recursive_feature_elimination()` создаем экземпляр модели *LinearRegression* под именем `estimator`. Далее создаем экземпляр модели *RFE (Recursive Feature Elimination)* под именем `rfe_model`, передавая `estimator` в качестве аргумента конструктора. Обучаем `rfe_model` на данных `X` и `Y`. Затем добавляем ранги признаков, полученные от `rfe_model`, в словарь `ranks` под ключом *'Recursive Feature Elimination'*, используя функцию `rank_to_dict_rfe()`.
```python
def recursive_feature_elimination():
# создание модели LinearRegression
estimator = LinearRegression()
# создание модели RFE
rfe_model = RFE(estimator)
rfe_model.fit(X, Y)
ranks['Recursive Feature Elimination'] = rank_to_dict_rfe(rfe_model.ranking_, names)
```
В функции `rank_to_dict_rfe(ranking, names)` находит обратные значения рангов признаков, делим 1 на каждый ранг. Округляем элементы полученного массива до двух знаков после запятой, используя функцию `round()`. И возвращаем словарь, где имена признаков из `names` являются ключами, а значениями являются округленные обратные значения рангов.
```python
def rank_to_dict_rfe(ranking, names):
# нахождение обратных значений рангов
n_ranks = [float(1 / i) for i in ranking]
# округление элементов массива
n_ranks = map(lambda x: round(x, 2), n_ranks)
# преобразование данных
return dict(zip(names, n_ranks))
```
#### Линейная корреляция (f_regression)
Для работы с линейной корреляцией выполним линейную корреляцию между признаками в матрице `X` и целевой переменной `Y`. Для этого используем функцию `f_regression()` для выполнения линейной регрессии между каждым признаком в матрице `X` и целевой переменной `Y`. После этого возвращается два массива: `correlation` содержит значения коэффициентов корреляции между признаками и `Y`, а `p_values` содержит соответствующие p-значения для каждого коэффициента корреляции. После чего создаем новую запись в словаре `ranks` с ключом *'linear correlation'*.
```python
correlation, p_values = f_regression(X, Y)
ranks['linear correlation'] = rank_to_dict(correlation, names)
```
### Вывод
Согласно условию задания значимыми параметрами были: *x1, x2, x3, x4, x5*. А зависимыми от них *x10, x11, x12, x13, x14*.
После сортировки полученных результатов работы моделей, можем увидеть следующее:
```
Ridge
[('x4', 1.0), ('x1', 0.98), ('x2', 0.8), ('x14', 0.61), ('x5', 0.54), ('x12', 0.39), ('x3', 0.25), ('x13', 0.19), ('x11', 0.16), ('x6', 0.08), ('x8', 0.07), ('x7', 0.02), ('x10', 0.02), ('x9', 0.0)]
Recursive Feature Elimination
[('x1', 1.0), ('x2', 1.0), ('x3', 1.0), ('x4', 1.0), ('x5', 1.0), ('x11', 1.0), ('x13', 1.0), ('x12', 0.5), ('x14', 0.33), ('x8', 0.25), ('x6', 0.2), ('x10', 0.17), ('x7', 0.14), ('x9', 0.12)]
linear correlation
[('x4', 1.0), ('x14', 0.98), ('x2', 0.45), ('x12', 0.44), ('x1', 0.3), ('x11', 0.29), ('x5', 0.04), ('x8', 0.02), ('x7', 0.01), ('x9', 0.01), ('x3', 0.0), ('x6', 0.0), ('x10', 0.0), ('x13', 0.0)]
```
Как можно заметить в модели *Ridge* параметры *x1, x2, x4, x5* имеют наибольшую значимость, что соответстует исходному условию задания, однако был потерян признак *x3*, значимость которого была показана низкой.
Модель *Recursive Feature Elimination* правильно показала все наиболее значиме признаки *x1, x2, x3, x4, x5*.
В модели линейной корреляции параметры *'x4'* и *'x14'* имеют наибольшую корреляцию, равную 1.0. Параметры *'x3', 'x6', 'x10' и 'x13'* имеют наименьшую корреляцию, равную 0.0. Таким образом, показав наихудний результат среди трех моделей.
В среднем по работе трех моделей имеем следующий результат, где параметр *'x4'* имеет наибольшую значимость, равную 1.0, параметр *'x9'* имеет наименьшую значимость, равную 0.04.:
```
Mean
[('x4', 1.0), ('x1', 0.76), ('x2', 0.75), ('x14', 0.64), ('x5', 0.53), ('x11', 0.48), ('x12', 0.44), ('x3', 0.42), ('x13', 0.4), ('x8', 0.11), ('x6', 0.09), ('x7', 0.06), ('x10', 0.06), ('x9', 0.04)]
```
Таким образом, можно сделать вывод о том, лучше всех справилась модель *Recursive Feature Elimination*, а также, что порядок значимости параметров может немного различаться в зависимости от модели.

View File

@ -0,0 +1,88 @@
from operator import itemgetter
import numpy as np
from sklearn.feature_selection import RFE, f_regression
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import Ridge
from sklearn.linear_model import LinearRegression
# генерируем исходные данные: 750 строк-наблюдений и 14 столбцов-признаков
np.random.seed(0)
size = 750
X = np.random.uniform(0, 1, (size, 14))
# Задаем функцию-выход: регрессионную проблему Фридмана
Y = (10 * np.sin(np.pi*X[:, 0]*X[:, 1]) + 20*(X[:, 2] - .5)**2 + 10*X[:, 3] + 5*X[:, 4]**5 + np.random.normal(0, 1))
# Добавляем зависимость признаков
X[:, 10:] = X[:, :4] + np.random.normal(0, .025, (size, 4))
names = ["x%s" % i for i in range(1, 15)] # - список признаков вида ['x1', 'x2', 'x3', ..., 'x14']
ranks = dict()
def rank_to_dict(ranks, names):
# получение абсолютных значений оценок(модуля)
ranks = np.abs(ranks)
minmax = MinMaxScaler()
# преобразование данных
ranks = minmax.fit_transform(np.array(ranks).reshape(14, 1)).ravel()
# округление элементов массива
ranks = map(lambda x: round(x, 2), ranks)
# преобразование данных
return dict(zip(names, ranks))
# Модель: случайное Лассо (RandomizedLasso) - устаревшее, поэтому используем Ridge-регрессия (Ridge Regression)
def ridge_regressions():
# Создание экземпляра модели Ridge
ridge_model = Ridge()
ridge_model.fit(X, Y)
ranks['Ridge'] = rank_to_dict(ridge_model.coef_, names)
# Модель: рекурсивное сокращение признаков (Recursive Feature Elimination RFE)
def recursive_feature_elimination():
# создание модели LinearRegression
estimator = LinearRegression()
# создание модели RFE
rfe_model = RFE(estimator)
rfe_model.fit(X, Y)
ranks['Recursive Feature Elimination'] = rank_to_dict_rfe(rfe_model.ranking_, names)
def rank_to_dict_rfe(ranking, names):
# нахождение обратных значений рангов
n_ranks = [float(1 / i) for i in ranking]
# округление элементов массива
n_ranks = map(lambda x: round(x, 2), n_ranks)
# преобразование данных
return dict(zip(names, n_ranks))
# Модель: линейная корреляция (f_regression)
def linear_correlation():
# вычисление линейной корреляции между X и y
correlation, p_values = f_regression(X, Y)
ranks['linear correlation'] = rank_to_dict(correlation, names)
if __name__ == '__main__':
ridge_regressions()
recursive_feature_elimination()
linear_correlation()
for key, value in ranks.items(): # Вывод нормализованных оценок важности признаков каждой модели
ranks[key] = sorted(value.items(), key=itemgetter(1), reverse=True)
for key, value in ranks.items():
print(key)
print(value)
mean = {} # - нахождение средних значений оценок важности по 3м моделям
for key, value in ranks.items():
for item in value:
if item[0] not in mean:
mean[item[0]] = 0
mean[item[0]] += item[1]
for key, value in mean.items():
res = value / len(ranks)
mean[key] = round(res, 2)
mean = sorted(mean.items(), key=itemgetter(1), reverse=True)
print("Mean")
print(mean)