Merge pull request 'romanova_adelina_lab_2 is ready' (#209) from romanova_adelina_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/IIS_2023_1/pulls/209
This commit is contained in:
commit
c2101d3e00
109
romanova_adelina_lab_2/README.md
Normal file
109
romanova_adelina_lab_2/README.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# Лабораторная работа №2. Вариант 21
|
||||||
|
|
||||||
|
## Тема:
|
||||||
|
Ранжирование признаков
|
||||||
|
|
||||||
|
## Модели:
|
||||||
|
|
||||||
|
- LinearRegression
|
||||||
|
- RandomizedLasso
|
||||||
|
- Recursive Feature Elimination (RFE)
|
||||||
|
|
||||||
|
## Как запустить программу:
|
||||||
|
Установить *python, numpy, matplotlib, sklearn*
|
||||||
|
```
|
||||||
|
python main.py --top_k=6
|
||||||
|
```
|
||||||
|
|
||||||
|
## Какие технологии использовались:
|
||||||
|
Язык программирования Python, библиотеки numpy, matplotlib, sklearn
|
||||||
|
|
||||||
|
Среда разработки VSCode
|
||||||
|
|
||||||
|
# Что делает лабораторная работа:
|
||||||
|
Генерирует данные и обучает такие модели, как: ```LinearRegression, RandomizedLasso, Recursive Feature Elimination (RFE)```
|
||||||
|
|
||||||
|
Производиться ранжирование признаков с помощью моделей ```LinearRegression, RandomizedLasso, Recursive Feature Elimination (RFE)```
|
||||||
|
|
||||||
|
Отображение получившихся результатов: top_k самых важных признака по среднему значению, значения признаков для каждой модели
|
||||||
|
|
||||||
|
Функция ```get_arguments()``` использует модуль argparse для обработки аргументов командной строки. В данном случае, скрипт ожидает аргумент --top_k, который по умолчанию установлен в 4.
|
||||||
|
|
||||||
|
```
|
||||||
|
def get_arguments():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument('--top_k', type=int, default=4, help='Кол-во самых выжных признаков')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
```
|
||||||
|
|
||||||
|
Функция ```data_gen()``` генерирует искусственные данные для регрессионной задачи. Здесь используется функция Фридмана для создания зависимости целевой переменной Y от признаков X.
|
||||||
|
|
||||||
|
```
|
||||||
|
def data_gen():
|
||||||
|
# --- генерируем исходные данные: 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))
|
||||||
|
|
||||||
|
return X, Y
|
||||||
|
```
|
||||||
|
|
||||||
|
Функция ```rank_to_dict()```, которая преобразует ранги признаков в словарь, нормализуя их значения от 0 до 1.
|
||||||
|
```
|
||||||
|
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))
|
||||||
|
```
|
||||||
|
|
||||||
|
Функция ```print_sorted_data()``` печатает отсортированные оценки признаков.
|
||||||
|
|
||||||
|
Функцию ```estimation()``` вычисляет средние оценки признаков на основе данных о рангах.
|
||||||
|
|
||||||
|
В блоке ```if __name__=="__main__":``` сначала получает аргументы командной строки, затем генерирует данные, обучает линейную регрессию, применяет рекурсивное сокращение признаков и случайное Лассо, а затем оценивает и выводит наиболее важные признаки с использованием средних оценок.
|
||||||
|
|
||||||
|
### Линейная регрессия (Linear Regression)
|
||||||
|
|
||||||
|
Это простой метод машинного обучения, который используется для прогнозирования непрерывной переменной на основе одной или нескольких других переменных. В нашем случае линейная регрессия используется для обучения модели на данных, а затем коэффициенты модели используются для ранжирования важности признаков
|
||||||
|
|
||||||
|
### Рекурсивное сокращение признаков (RFE)
|
||||||
|
|
||||||
|
Это метод выбора признаков, который работает путем итеративного удаления признаков и переобучения модели, чтобы определить, какие признаки наиболее важны для предсказания. В коде RFE используется для обучения модели на данных, а затем ранжирование признаков модели используется для ранжирования важности признаков
|
||||||
|
|
||||||
|
### Случайное Лассо (Randomized Lasso)
|
||||||
|
|
||||||
|
Это метод регуляризации, который добавляет штраф к коэффициентам модели в зависимости от их величины. Это делается для предотвращения переобучения модели. В коде случайное Лассо используется для обучения модели на данных, а затем коэффициенты модели используются для ранжирования важности признаков
|
||||||
|
|
||||||
|
|
||||||
|
## Оценка работы моделей
|
||||||
|
|
||||||
|
```
|
||||||
|
{'x1': 0.33, 'x4': 0.23, 'x2': 0.2, 'x11': 0.2, 'x3': 0.17, 'x13': 0.16, 'x5': 0.06, 'x12': 0.06, 'x14': 0.04, 'x6': 0.01, 'x8': 0.01, 'x7': 0.0, 'x9': 0.0, 'x10': 0.0}
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
Параметр - x1, значение - 0.33
|
||||||
|
Параметр - x4, значение - 0.23
|
||||||
|
Параметр - x2, значение - 0.2
|
||||||
|
Параметр - x11, значение - 0.2
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Linear reg
|
||||||
|
[('x1', 1.0), ('x4', 0.69), ('x2', 0.61), ('x11', 0.59), ('x3', 0.51), ('x13', 0.48), ('x5', 0.19), ('x12', 0.19), ('x14', 0.12), ('x8', 0.03), ('x6', 0.02), ('x10', 0.01), ('x7', 0.0), ('x9', 0.0)]
|
||||||
|
RFE
|
||||||
|
[('x9', 1.0), ('x7', 0.86), ('x10', 0.71), ('x6', 0.57), ('x8', 0.43), ('x14', 0.29), ('x12', 0.14), ('x1', 0.0), ('x2', 0.0), ('x3', 0.0), ('x4', 0.0), ('x5', 0.0), ('x11', 0.0), ('x13', 0.0)]
|
||||||
|
RandomizedLasso
|
||||||
|
[('x4', 1.0), ('x2', 0.37), ('x1', 0.36), ('x5', 0.32), ('x6', 0.02), ('x8', 0.02), ('x3', 0.01), ('x7', 0.0), ('x9', 0.0), ('x10', 0.0), ('x11', 0.0), ('x12', 0.0), ('x13', 0.0), ('x14', 0.0)]
|
||||||
|
```
|
||||||
|
|
||||||
|
Хуже всех показала себя модель случайного Лассо, потеряв три значимые признака и добавив один лишний. Модели линейной регрессии и RFE допустили по одной ошибке, однако последняя не потеряла ни одного значимого признака. Значимость в среднем получилась неудовлетворительной и выдала три ошибки, как и первая модель.
|
||||||
|
|
||||||
|
Исходя из этого, можно сделать вывод, что для ранжирования признаков лучше использовать специально созданные для этого инструменты по типу RFE, а не использовать коэффициенты признаков регрессионных моделей.
|
76
romanova_adelina_lab_2/RandomizedLass.py
Normal file
76
romanova_adelina_lab_2/RandomizedLass.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
from sklearn.utils import check_X_y, check_random_state
|
||||||
|
from sklearn.linear_model import Lasso
|
||||||
|
from scipy.sparse import issparse
|
||||||
|
from scipy import sparse
|
||||||
|
|
||||||
|
|
||||||
|
def _rescale_data(x, weights):
|
||||||
|
if issparse(x):
|
||||||
|
size = weights.shape[0]
|
||||||
|
weight_dia = sparse.dia_matrix((1 - weights, 0), (size, size))
|
||||||
|
x_rescaled = x * weight_dia
|
||||||
|
else:
|
||||||
|
x_rescaled = x * (1 - weights)
|
||||||
|
|
||||||
|
return x_rescaled
|
||||||
|
|
||||||
|
|
||||||
|
class RandomizedLasso(Lasso):
|
||||||
|
"""
|
||||||
|
Randomized version of scikit-learns Lasso class.
|
||||||
|
|
||||||
|
Randomized LASSO is a generalization of the LASSO. The LASSO penalises
|
||||||
|
the absolute value of the coefficients with a penalty term proportional
|
||||||
|
to `alpha`, but the randomized LASSO changes the penalty to a randomly
|
||||||
|
chosen value in the range `[alpha, alpha/weakness]`.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
weakness : float
|
||||||
|
Weakness value for randomized LASSO. Must be in (0, 1].
|
||||||
|
|
||||||
|
See also
|
||||||
|
--------
|
||||||
|
sklearn.linear_model.LogisticRegression : learns logistic regression models
|
||||||
|
using the same algorithm.
|
||||||
|
"""
|
||||||
|
def __init__(self, weakness=0.5, alpha=1.0, fit_intercept=True,
|
||||||
|
precompute=False, copy_X=True, max_iter=1000,
|
||||||
|
tol=1e-4, warm_start=False, positive=False,
|
||||||
|
random_state=None, selection='cyclic'):
|
||||||
|
self.weakness = weakness
|
||||||
|
super(RandomizedLasso, self).__init__(
|
||||||
|
alpha=alpha, fit_intercept=fit_intercept, precompute=precompute, copy_X=copy_X,
|
||||||
|
max_iter=max_iter, tol=tol, warm_start=warm_start,
|
||||||
|
positive=positive, random_state=random_state,
|
||||||
|
selection=selection)
|
||||||
|
|
||||||
|
def fit(self, X, y):
|
||||||
|
"""Fit the model according to the given training data.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
X : {array-like, sparse matrix}, shape = [n_samples, n_features]
|
||||||
|
The training input samples.
|
||||||
|
|
||||||
|
y : array-like, shape = [n_samples]
|
||||||
|
The target values.
|
||||||
|
"""
|
||||||
|
if not isinstance(self.weakness, float) or not (0.0 < self.weakness <= 1.0):
|
||||||
|
raise ValueError('weakness should be a float in (0, 1], got %s' % self.weakness)
|
||||||
|
|
||||||
|
X, y = check_X_y(X, y, accept_sparse=True)
|
||||||
|
|
||||||
|
n_features = X.shape[1]
|
||||||
|
weakness = 1. - self.weakness
|
||||||
|
random_state = check_random_state(self.random_state)
|
||||||
|
|
||||||
|
weights = weakness * random_state.randint(0, 1 + 1, size=(n_features,))
|
||||||
|
|
||||||
|
# TODO: I am afraid this will do double normalization if set to true
|
||||||
|
#X, y, _, _ = _preprocess_data(X, y, self.fit_intercept, normalize=self.normalize, copy=False,
|
||||||
|
# sample_weight=None, return_mean=False)
|
||||||
|
|
||||||
|
# TODO: Check if this is a problem if it happens before standardization
|
||||||
|
X_rescaled = _rescale_data(X, weights)
|
||||||
|
return super(RandomizedLasso, self).fit(X_rescaled, y)
|
106
romanova_adelina_lab_2/main.py
Normal file
106
romanova_adelina_lab_2/main.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
from RandomizedLass import RandomizedLasso
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from sklearn.linear_model import LinearRegression, Ridge, Lasso
|
||||||
|
from sklearn.feature_selection import RFE
|
||||||
|
|
||||||
|
from sklearn.preprocessing import MinMaxScaler
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def get_arguments():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument('--top_k', type=int, default=4, help='Кол-во самых выжных признаков')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def data_gen():
|
||||||
|
# --- генерируем исходные данные: 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))
|
||||||
|
|
||||||
|
return X, Y
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
||||||
|
def print_sorted_data(ranks: dict):
|
||||||
|
print()
|
||||||
|
for key, value in ranks.items():
|
||||||
|
ranks[key] = sorted(value.items(), key=lambda item: item[1], reverse=True)
|
||||||
|
for key, value in ranks.items():
|
||||||
|
print(key)
|
||||||
|
print(value)
|
||||||
|
|
||||||
|
|
||||||
|
def estimation(ranks: dict, top_k):
|
||||||
|
#Создаем пустой список для данных
|
||||||
|
mean = {}
|
||||||
|
#«Бежим» по списку ranks
|
||||||
|
for key, value in ranks.items():
|
||||||
|
#«Пробегаемся» по списку значений ranks, которые являются парой имя:оценка
|
||||||
|
for item in value.items():
|
||||||
|
#имя будет ключом для нашего mean
|
||||||
|
#если элемента с текущим ключем в mean нет - добавляем
|
||||||
|
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 = dict(sorted(mean.items(), key=lambda x:x[1], reverse=True))
|
||||||
|
print("sorted MEAN")
|
||||||
|
print(mean_sorted, '---'*25, sep='\n')
|
||||||
|
|
||||||
|
for item in list(mean_sorted.items())[:top_k]:
|
||||||
|
print(f'Параметр - {item[0]}, значение - {item[1]}')
|
||||||
|
print('---'*25)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
args = get_arguments()
|
||||||
|
|
||||||
|
X,Y = data_gen()
|
||||||
|
|
||||||
|
# Линейная модель
|
||||||
|
lr = LinearRegression()
|
||||||
|
lr.fit(X, Y)
|
||||||
|
|
||||||
|
# Рекурсивное сокращение признаков
|
||||||
|
rfe = RFE(lr)
|
||||||
|
rfe.fit(X, Y)
|
||||||
|
|
||||||
|
# Случайное Лассо
|
||||||
|
randomized_lasso = RandomizedLasso(alpha=.01)
|
||||||
|
randomized_lasso.fit(X, Y)
|
||||||
|
|
||||||
|
names = ["x%s" % i for i in range(1,15)]
|
||||||
|
|
||||||
|
ranks = {}
|
||||||
|
|
||||||
|
ranks["Linear reg"] = rank_to_dict(lr.coef_, names)
|
||||||
|
ranks["RFE"] = rank_to_dict(rfe.ranking_, names)
|
||||||
|
ranks["RandomizedLasso"] = rank_to_dict(randomized_lasso.coef_, names)
|
||||||
|
|
||||||
|
estimation(ranks, args.top_k)
|
||||||
|
|
||||||
|
print_sorted_data(ranks)
|
17
romanova_adelina_lab_2/req.txt
Normal file
17
romanova_adelina_lab_2/req.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
contourpy==1.2.0
|
||||||
|
cycler==0.12.1
|
||||||
|
fonttools==4.45.1
|
||||||
|
importlib-resources==6.1.1
|
||||||
|
joblib==1.3.2
|
||||||
|
kiwisolver==1.4.5
|
||||||
|
matplotlib==3.8.2
|
||||||
|
numpy==1.26.2
|
||||||
|
packaging==23.2
|
||||||
|
Pillow==10.1.0
|
||||||
|
pyparsing==3.1.1
|
||||||
|
python-dateutil==2.8.2
|
||||||
|
scikit-learn==1.3.2
|
||||||
|
scipy==1.11.4
|
||||||
|
six==1.16.0
|
||||||
|
threadpoolctl==3.2.0
|
||||||
|
zipp==3.17.0
|
Loading…
Reference in New Issue
Block a user