<jupyter_start><jupyter_text>Úkol č. 4 - regrese (do 2. ledna)  * Cílem tohoto úkolu je vyzkoušet si řešit regresní problém na reálných (ale celkem vyčištěných) datech.  > **Nejdůležitější na úkolu je to, abyste udělali vše procesně správně: korektní rozdělení datasetu, ladění hyperparametrů, vyhodnocení výsledků atp.** Dataset  * Zdrojem dat je list *Data* v souboru `Residential-Building-Data-Set.xlsx` na course pages (originál zde: https://archive.ics.uci.edu/ml/datasets/Residential+Building+Data+Set).  * Popis datasetu najdete na listu *Descriptions* ve stejném souboru.   Pokyny k vypracování  1. Rozdělte data na trénovací a testovací množinu.  1. Proveďte základní průzkum dat a příp. vyhoďte nezajímavé příznaky.  1. Aplikujte lineární a hřebenovou regresi a výsledky řádně vyhodnoťte:    * K měření chyby použijte `mean_absolute_error`.    * Experimentujte s tvorbou nových příznaků (na základě těch dostupných).    * Experimentujte se standardizací/normalizací dat.    * Vyberte si hyperparametry modelů k ladění a najděte jejich nejlepší hodnoty.  1. Použijte i jiný model než jen lineární a hřebenovou regresi. Poznámky k odevzdání  * Řiďte se pokyny ze stránky https://courses.fit.cvut.cz/BI-VZD/homeworks/index.html.  * Odevzdejte pouze tento Jupyter Notebook, opravujíví by neměl nic jiného potřebovat.  * Opravující Vám může umožnit úkol dodělat či opravit a získat tak další body. První verze je ale důležitá a bude-li odbytá, budete za to penalizováni.  Funkce na načtení datV této úloze máme predikovat dvě výsledné hodnoty "sales price" a "construction costs" na základě hodnot ekonomických faktorů a faktorů spojených přímo s projektem. Já jsem se rozhodl vždy trénovat 2 modely zvlášt, jeden který bude predikovat "sales price" a druhý který bude predikovat "construction costs". Výsledné MAE spočítám sečtením chyb každého z modelu a vydělením počtem modelů.<jupyter_code>from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.ensemble import RandomForestRegressor

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
warnings.filterwarnings('ignore', module='sklearn')

def LoadData():
    path = 'Residential-Building-Data-Set.xlsx'
    import pandas
    df = pandas.read_excel(path,header=1)
    return df


def ConvertToArray(df):
    X, Y1, Y2 = [], [], []
    n_of_rows = len(df.index)

    for i in range(n_of_rows):
        entire_row = list(df.iloc[i])
        X.append(entire_row[:-2])
        Y1.append(entire_row[-2])
        Y2.append(entire_row[-1])

    return X, Y1, Y2<jupyter_output><empty_output><jupyter_text>Testování na Hrubých datechPoté co jsme dataset rozdělili na trénovací a testovací množinu budeme nejprve exkluzivně pracovat s trénovací množinou. Na ní zjistíme úspěšnost našich modelů za použití cross validation.<jupyter_code>print('Results without any normalization or feature engineering:')

df = LoadData()
X, Y1, Y2 = ConvertToArray(df)
delimiter = 330

x_test = X[delimiter:]
y1_test = Y1[delimiter:]
y2_test = Y2[delimiter:]

X = X[:delimiter]
Y1 = Y1[:delimiter]
Y2 = Y2[:delimiter]

reg = LinearRegression()
res = abs(cross_val_score(reg, X, Y1,cv=10,scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(reg, X, Y2,cv=10,scoring='neg_mean_absolute_error').mean())
res /= 2.0
print('Linear Regression MAE:',res)

ridge = Ridge()
res = abs(cross_val_score(ridge, X, Y1,cv=10,scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(ridge, X, Y2,cv=10,scoring='neg_mean_absolute_error').mean())
res /= 2.0
print('Ridge Regression MAE:',res)

regr = RandomForestRegressor(max_depth=8, random_state=0)
res = abs(cross_val_score(regr, X, Y1,cv=10,scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(regr, X, Y2,cv=10,scoring='neg_mean_absolute_error').mean())
res /= 2.0
print('Random Forest MAE:',res)<jupyter_output>Results without any normalization or feature engineering:
Linear Regression MAE: 78.2963000618238
Ridge Regression MAE: 77.89365490770658
Random Forest MAE: 101.15629723050026<jupyter_text>Feature selectionV datasetu pracujeme s časovými daty. Kdy například feature V-25.3 je hodnota stejné featury jako V-25.1 jen v jiném čase. První funkce TakeFinalFeature zahodí všechny ostatní hodnoty dané feature kromě té poslední. Toto je důležité protože Linearní regrese funguje lépe pokud jsou feature mezi sebou nekorelované.Funkce SelectFeatures zjistí, které znaky jsou nejméně korelované s hodnotami které máme predikovat a tyto feature odstraní. Funkce zjistila že znaky "Lot area" a "Population of the city" mají nejmenší vliv na výsledek.<jupyter_code>def TakeFinalFeature(df):
    for i in range(11,29):
        df = df.drop(['V-'+str(i)],axis=1)
        for j in range(1,4):
            df = df.drop(['V-'+str(i)+'.'+str(j)],axis=1)

    return df
def SelectFeatures(df):
    cor = df.corr()
    cor_target1 = abs(cor['V-9'])
    relevant_features1 = cor_target1[cor_target1 < 0.2]

    cor_target2 = abs(cor['V-10'])
    relevant_features2 = cor_target2[cor_target2 < 0.2]

    print(relevant_features1)
    print(relevant_features2)

    df.drop(['V-3','V-28.4'], axis=1)

    return df<jupyter_output><empty_output><jupyter_text>Testování na datech po feature selectionPo aplikování feature selection vidíme, že MAE (mean absolute error) klesl skoro o 10 bodů (zhruba 10%) u všech modelů na trénovací množině.<jupyter_code>print('Results with feature selection:')

df = LoadData()
df = SelectFeatures(df)
df = TakeFinalFeature(df)
X, Y1, Y2 = ConvertToArray(df)

x_test = X[delimiter:]
y1_test = Y1[delimiter:]
y2_test = Y2[delimiter:]

X = X[:delimiter]
Y1 = Y1[:delimiter]
Y2 = Y2[:delimiter]

reg = LinearRegression()
res = abs(cross_val_score(reg, X, Y1,cv=10,scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(reg, X, Y2,cv=10,scoring='neg_mean_absolute_error').mean())
res /= 2.0
print('Linear Regression MAE:',res)

ridge = Ridge()
res = abs(cross_val_score(ridge, X, Y1,cv=10,scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(ridge, X, Y2,cv=10,scoring='neg_mean_absolute_error').mean())
res /= 2.0
print('Ridge Regression MAE:',res)

regr = RandomForestRegressor(max_depth=8, random_state=0)
res = abs(cross_val_score(regr, X, Y1,cv=10,scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(regr, X, Y2,cv=10,scoring='neg_mean_absolute_error').mean())
res /= 2.0
print('Random Forest MAE:',res)<jupyter_output>Results with feature selection:
START QUARTER         0.093030
COMPLETION QUARTER    0.003542
V-3                   0.163545
V-6                   0.192130
V-7                   0.139202
V-28.1                0.197168
V-28.2                0.131822
V-28.3                0.040370
V-20.4                0.127425
Name: V-9, dtype: float64
START QUARTER         0.128630
COMPLETION QUARTER    0.061487
V-3                   0.165161
V-28.2                0.126206
V-28.3                0.041004
Name: V-10, dtype: float64
Linear Regression MAE: 69.51894367788947
Ridge Regression MAE: 69.37704198079169
Random Forest MAE: 94.29449858351447<jupyter_text>Standardizace datV data science se často vyplatí dělat takzvaná standardizace dat. V našem případě využijeme StandardScaler z knihovny sklearn, který pro každý data feature spočítá průmernou hodnotu a rozptyl. Od každé hodnoty dané feature nejprve odečte medián (tím zajistí vycentrování dat) a následně hodnotu vydělí rozptylem.<jupyter_code>from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X)
transformedX = scaler.transform(X)

res = abs(cross_val_score(reg, transformedX, Y1, cv=10, scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(reg, transformedX, Y2, cv=10, scoring='neg_mean_absolute_error').mean())
print('Linear regression MAE after standardization:',res/2.0)

res = abs(cross_val_score(ridge, transformedX, Y1, cv=10, scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(ridge, transformedX, Y2, cv=10, scoring='neg_mean_absolute_error').mean())
print('Ridge regression MAE after standardization:',res/2.0)

res = abs(cross_val_score(regr, transformedX, Y1, cv=10, scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(regr, transformedX, Y2, cv=10, scoring='neg_mean_absolute_error').mean())
print('Random forest MAE after standardization:',res/2.0)<jupyter_output>Linear regression MAE after standardization: 69.55362524912306
Ridge regression MAE after standardization: 65.5800414061934
Random forest MAE after standardization: 95.35921156648433<jupyter_text>Výsledky standardizaceV našem případě nastala pozitivní změna výsledku kvůli standardizaci pouze u Random Forest, tudíž v našem případě nebudeme nadále standardizaci používat. Kdybychom jí ale chtěli na testovací množinu přeciejn použít, nebudeme testovací množinu znovu fitovat, ale zavoláme pouze funkci transform.   Hledání hyperparametrůPo nastavení stavového prostoru hyperparametrů tento prostor náhodně prohledáme. Celý stavový prostor má 4*2*5*3*3*2=720 možností. Abychom ušetřili čas, vykoušíme náhodně pouze 100 možností.<jupyter_code># Find hyperparameters
import numpy as np
from sklearn.model_selection import RandomizedSearchCV

n_estimators = [20,50,100,200]
max_features = ['auto', 'sqrt']
max_depth = [3,6,10,30]
max_depth.append(None)
min_samples_split = [2, 5, 10]
min_samples_leaf = [1, 2, 4]
bootstrap = [True, False]

# Create the random grid
random_grid = {'n_estimators': n_estimators,
               'max_features': max_features,
               'max_depth': max_depth,
               'min_samples_split': min_samples_split,
               'min_samples_leaf': min_samples_leaf,
               'bootstrap': bootstrap}

grid_search = RandomizedSearchCV(estimator = regr, param_distributions = random_grid, n_iter = 100, cv = 3, verbose=0, random_state=42, n_jobs = 1)
grid_search.fit(X,Y1)<jupyter_output><empty_output><jupyter_text>Nalezené hyperparametryZde vidíte nalezené nejlepší parametry a také výsledné MAE na trénovací množině, které je o 3 nižší než s defaultně nastavenými parametry.<jupyter_code>print('Best parameters:',grid_search.best_params_)

regr = RandomForestRegressor(**(grid_search.best_params_))

res = abs(cross_val_score(regr, X, Y1, cv=10, scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(regr, X, Y2, cv=10, scoring='neg_mean_absolute_error').mean())
print('Best Random forest MAE:',res/2.0)<jupyter_output>Best parameters: {'n_estimators': 50, 'min_samples_split': 2, 'min_samples_leaf': 1, 'max_features': 'auto', 'max_depth': 10, 'bootstrap': True}
Best Random forest MAE: 88.406702849819<jupyter_text>Hledání hyperparametrůObdobně budeme postupovat při hledání hyperparametrů i pro hřebenovou regresi.<jupyter_code>from sklearn.model_selection import GridSearchCV

solver = ['svd', 'cholesky', 'lsqr', 'sparse_cg', 'sag', 'saga']
alpha = [0.001, 0.05,0.2,1,3,10,100,300]

# Create the random grid for ridge regression
random_grid_ridge = {'solver': solver,
               'alpha': alpha,}

grid_search = GridSearchCV(estimator = ridge, param_grid = random_grid_ridge, cv = 5, verbose=0)
grid_search.fit(X,Y1)<jupyter_output><empty_output><jupyter_text>Nalezené hyperparametryOpět vidíme pozitivní efekt použití nalezených hyperparametrů na výsledné MAE na trénovací množině oproti defaultně nastaveným parametrům.<jupyter_code>print('Best parameters for ridge regression:',grid_search.best_params_)

ridge = Ridge(**(grid_search.best_params_))

res = abs(cross_val_score(ridge, X, Y1, cv=10, scoring='neg_mean_absolute_error').mean()) + abs(cross_val_score(ridge, X, Y2, cv=10, scoring='neg_mean_absolute_error').mean())
print('Best Ridge regression MAE:',res/2.0)<jupyter_output>Best parameters for ridge regression: {'alpha': 100, 'solver': 'cholesky'}
Best Ridge regression MAE: 67.28294612570365<jupyter_text>Výsledek na testovací množiněNa závěr naše modely vyhodnotíme na testovací množině, kterou jsme měli celou dobu stranou.<jupyter_code>from sklearn.metrics import mean_absolute_error
res = 0

regr.fit(X,Y1)
pred = regr.predict(x_test)
res += mean_absolute_error(pred,y1_test)

regr.fit(X,Y2)
pred = regr.predict(x_test)
res += mean_absolute_error(pred,y2_test)

print('Best Random forest MAE on a testing set:',res/2.0)

res = 0
ridge.fit(X,Y1)
pred = ridge.predict(x_test)
res += mean_absolute_error(pred,y1_test)

ridge.fit(X,Y2)
pred = ridge.predict(x_test)
res += mean_absolute_error(pred,y2_test)
print('Best Ridge regression on a testing set:',res/2.0)<jupyter_output>Best Random forest MAE on a testing set: 66.37749509314746
Best Ridge regression on a testing set: 54.77164935031843
{'fraction_non_alphanumeric': 0.1043080321918889, 'fraction_numerical': 0.04686760296670349, 'mean_word_length': 5.460244648318043, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 36, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '366882', 'license': 'MIT', 'n_tokens_mistral': 5181, 'n_tokens_neox': 4892, 'n_words': 1140, 'path': 'VZD/04/homework_04_B191.ipynb', 'repo_name': 'JahodaPaul/FIT_CTU', 'size': '12674'}