Jos koneoppiminen ja sklearn (scikit-learn) -kirjasto ovat sinulle täysin uusia, niin lue ennen tätä artikkelia Lineaarinen regressio 1 ja Lineaarinen regressio 2.
Tämän artikkelin ohjelmakoodin ja tulosteet löydät GitHubista:
https://github.com/taanila/tilastoapu/blob/master/linreg3.ipynb
Jos kopioit koodia itsellesi, niin kannattaa käyttää GitHubia. Tästä artikkelista kopioidut koodit eivät välttämättä toimi oikein.
Tämän artikkelin esimerkkidatana käytän sklearn kirjastosta löytyvää dataa Bostonin asuntojen hinnoista. Artikkelin ideat olen lainannut osoitteesta
https://towardsdatascience.com/linear-regression-on-boston-housing-dataset-f409b7e4a155
Ohjelmakirjastojen tuonti
Kuvailevasta analyysistä tutut peruskirjastot ovat tarpeen:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns %matplotlib inline
Datan valmistelu
Lataan esimerkkiaineiston sklearn-kirjastosta seuraavasti:
from sklearn.datasets import load_boston boston_data = load_boston()
Aineistossa on eroteltu data (features), target (selitettävä muuttuja), feature_names (selittävien muuttujien nimet) ja DESCR (aineiston kuvaus). Tämä selviää keys()-funktiolla:
print(boston_data.keys())
dict_keys([’data’, ’target’, ’feature_names’, ’DESCR’])
Komennolla print(boston_data.DESCR) voit lukea aineiston kuvauksen.
Luon aineiston perusteella boston-nimisen dataframen:
boston = pd.DataFrame(boston_data.data, columns=boston_data.feature_names) boston['MEDV'] = boston_data.target boston.head()
Selitettävä muuttuja MEDV kuvaa asuntojen mediaanihintoja tuhansina dollareina eri alueilla. Muut muuttujat ovat ehdokkaita mediaanihintaa selittäviksi muuttujiksi (voit lukea lisää aineiston kuvauksesta).
Varmuuden vuoksi kannattaa tarkistaa, onko datassa puuttuvia arvoja:
boston.isnull().sum()
Puuttuvia arvoja ei ole, mikä on hyvä asia.
Seuraavaksi katson miten selitettävän muuttujan MEDV arvot ovat jakautuneet. Käytän tarkasteluun seaborn-kirjaston distplot-kuviota ja luokittelen hinnat 30 luokkaan:
sns.distplot(boston['MEDV'], bins=30)
Kuvion mukaan asuntojen mediaanihintojen jakauma on oikealle vino. Vinoudesta huolimatta yritetään mallintaa hintoja lineaarisella regressiolla.
Selittävien muuttujien valinnassa voin hyödyntää korrelaatiokertoimia. Seaborn-kirjaston heatmap-funktiolla voin värjätä corr()-funktiolla lasketut korrelaatiokertoimet niiden arvon mukaan. Oletusarvolla heatmap tulostaa vain värilliset ruudut, mutta lisäparametrilla annot=True saan myös korrelaatiokertoimien arvot näkyviin:
correlation_matrix = boston.corr().round(2) plt.figure(figsize=(12,9)) sns.heatmap(data=correlation_matrix, annot=True)
Kaikkein eniten MEDV-muuttujan kanssa korreloivat RM (0,7) ja LSTAT (-0,74). Katson vielä kuvion avulla miltä kyseisten muuttujien korrelaatio MEDV-muuttujan kanssa näyttää. Seuraava koodi toimii vaikka lisäisit enemmänkin muuttujia features-listaan:
features = ['LSTAT', 'RM'] target = boston['MEDV'] plt.figure(figsize=(10, 5)) for i, col in enumerate(features): plt.subplot(1, len(features) , i+1) plt.scatter(boston[col], target) plt.xlabel(col) plt.ylabel('MEDV')
Riippuvuus on selkeää molemmissa tapauksissa, mutta erityisesti LSTAT-muuttujan kohdalla riippuvuus ei ole täysin suoraviivaista. Tästä huolimatta jatketaan eteenpäin.
Valmistellaan vielä X (features-matriisi) ja y (target) mallintamista varten:
X = boston[['LSTAT', 'RM']] y = boston['MEDV']
Opetusdata ja testidata
Jos dataa on riittävästi, niin kannattaa jakaa se opetusdataan ja testidataan. Testidatan avulla voidaan arvioida opetusdatan perusteella laadittua mallia. Seuraavassa jaan datan sattumanvaraisesti opetusdataan ja testidataan (20 % datasta). Parametri random_state asettaa satunnaislukugeneraattorin siemenluvun. Jos jätän sen asettamatta, niin saan eri kerroilla erilaisen jaon opetusdataan ja testidataan. Käyttämällä samaa siemenarvoa saan aina saman jaon.
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state=5) print(X_train.shape) print(X_test.shape)
(404, 2)
(102, 2)
Huomaan, että opetusdatassa on 404 havaintoa ja testidatassa 102 havaintoa.
Mallin sovitus
Mallin sovitukseen kuuluu LinearRegression-mallin tuonti lineaaristen mallien kirjastosta ja mallin sovitus fit-funktiolla.
from sklearn.linear_model import LinearRegression malli = LinearRegression() malli.fit(X_train, y_train)
Tuloksesta näen mallin lähtötiedot, joita olisin halutessani voinut säätää:
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
Mallin sopivuuden arviointi
Mallin sopivuutta arvioin keskivirheen ja selityskertoimen avulla.
RMSE (root mean squared error) eli keskivirhe lasketaan seuraavasti: lasketaan toteutuneiden havaintojen ja mallin ennustamien arvojen erotusten neliöt yhteen ja jaetaan havaintojen lukumäärällä (mean squared error); lopuksi otetaan neliöjuuri.
Mean squared error löytyy sklearn.metrics-kirjastosta:
from sklearn.metrics import mean_squared_error y_train_predict = malli.predict(X_train) rmse = (np.sqrt(mean_squared_error(y_train, y_train_predict))) r2 = malli.score(X_train,y_train) print('Mallin sopivuus opetusdataan') print("--------------------------------------") print('Keskivirhe: {}'.format(rmse)) print('Selityskerroin: {}'.format(r2)) print("n") y_test_predict = malli.predict(X_test) rmse = (np.sqrt(mean_squared_error(y_test, y_test_predict))) r2 = malli.score(X_test, y_test) print('Mallin sopivuus testidataan') print('--------------------------------------') print('Keskivirhe: {}'.format(rmse)) print('Selityskerroin: {}'.format(r2))
Mallin sopivuus opetusdataan
————————————–
Keskivirhe: 5.6371293350711955
Selityskerroin: 0.6300745149331701
Mallin sopivuus testidataan
————————————–
Keskivirhe: 5.137400784702911
Selityskerroin: 0.6628996975186953
Katson vielä kuviona, miten hyvin ennustaminen onnistuu testidatassa:
plt.scatter(y_test, y_test_predict) plt.xlabel('y_test') plt.ylabel('y_test_predict')
Malli on sitä parempi, mitä lähempänä suoraa viivaa kuvion pisteet ovat.
Mallin parantaminen
Edellä esimerkkinä laskettu malli on kelvollinen, mutta voisin parantaa mallia monin tavoin:
Asuntojen mediaanihinnat eivät ole normaalisti jakautuneet (jakauma on oikealle vino). Mallin parantamiseksi voisin kokeilla jompaa kumpaa seuraavista:
- pudotan jakauman yläpään suurimmat mediaanihinnat pois opetusdatasta
- muunnan mediaanihintoja paremmin normaalijakaumaa vastaaviksi.
Selittävien muuttujien ja mediaanihintojen välinen riippuvuus ei ollut aivan suoraviivainen. Tämä saattaisi korjaantua, jos muunnan mediaanihinnat paremmin normaalijakaumaa vastaaviksi. Tarvittaessa voin tehdä myös selittäville muuttujille muunnoksia tai voin valita malliksi ei-lineaarisen mallin.
Malliin voin myös lisätä selittäviä muuttujia. PTRATIO-muuttujan ottamista selittäväksi muuttujaksi kannattaisi kokeilla.
Jos teen korjauksia malliin, niin voin arvioida korjatun mallin sopivuutta vertaamalla sen keskivirhettä ja selityskerrointa tämän artikkelin malliin.