Път за прилагане на модел на линейна регресия чрез A-Z предварителна обработка

Този урок включва подход стъпка по стъпка за изграждане на линеен регресионен модел към набор от данни. Наборът от данни, който се използва за урока, е достъпен по-долу.



Нека вземем следния случай на употреба за лесно разбиране на потока на този урок.

Случай на употреба: Има ли връзка между влажността и температурата? Какво ще кажете за влажността и видимата температура? Можете ли да предскажете видимата температура предвид влажността?

Когато става въпрос за обучение на модел за машинно обучение, предварителната обработка на набора от данни преди обучение на модела е от съществено значение. Тъй като избраният набор от данни може да включва липсващи стойности, извънредни стойности, изкривени разпределения и категориални характеристики, които трябва да бъдат предварително обработени преди обучение на модела.

В този урок ще обсъдя какво трябва да бъде предварително обработено, как да ги идентифицираме и как да ги обработим предварително. Да започнем предварителната обработка от A до Z.

Откриване на набора от данни

Преди да започнем с случая на употреба, трябва да проучим набора от данни за по-добро разбиране.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from google.colab import drive
drive.mount("/content/gdrive")
weather_dataset = pd.read_csv("/content/gdrive/My Drive/ML Data/weatherHistory.csv")
weather_dataset.head(5)

Като първа стъпка наборът от данни е зареден като рамка с данни с помощта на функцията pd.read_csv() и показва първите 5 реда от рамката с данни.

Освен това открийте набора от данни, като използвате функциите .info() и .describe(), които са налични в dataframe.

weather_dataset.info()

От горните подробности можем да наблюдаваме характеристики като

  • колко реда и колони има в набора от данни
  • какви са типовете данни на функциите
  • колко категориални и числови характеристики има в набора от данни.
weather_dataset.describe()

От горните подробности можем да открием, че всички статистики в колоната „Loud Cover“ са „0“. Всички редове имат „0“ като стойност (уникална стойност). Следователно тази функция не е полезна за обучение на модела. Така че можем да премахнем тази функция, преди да започнем предварителната обработка.

weather_dataset['Loud Cover'].value_counts()

Също така изтрих функцията „Ежедневно резюме“, тъй като тази функция съдържа уникално времево клеймо за всеки ред. Ако планирате да използвате тази функция, можете да използвате тази колона, за да извлечете годината или месеца от клеймото за време.

weather_dataset=weather_dataset.drop(['Loud Cover','Daily Summary'],axis=1)

Обработка на липсващи стойности

Първо трябва да установим дали има липсващи стойности в набора от данни. По-долу кодовият сегмент ще покаже броя на липсващите стойности, които са налични във всяка колона на рамката с данни.

weather_dataset.isnull().sum()

След това можем да използваме функцията .value_counts(), за да получим броя на стойностите на функцията „Тип валеж“.

weather_dataset['Precip Type'].value_counts()

Най-добрият вариант за справяне с липсващите данни е чрез премахване на редовете, които имат липсващи стойности. Въпреки това, когато премахваме липсващи стойности, трябва да внимаваме за количеството данни, които могат да бъдат премахнати. В този сценарий изпускам 517 реда от целия набор от данни, който има 96453 реда.

df1 = weather_dataset.dropna()

След като премахнете липсващите данни, проверете формата на новата рамка с данни и също така проверете дали всички липсващи стойности са премахнати.

print(f"shape of the 'weather_dataset' dataframe : {weather_dataset.shape}")
print(f"shaped of the 'df1' dataframe : {df1.shape}")

df1.isnull().sum()

Боравене с отклонения

Първо трябва да определим дали има отклонения във всяка колона. За да идентифицираме извънредни стойности, можем да начертаем диаграми с кутии за всяка колона с данни, налични в „df1“ Dataframe.

plt.figure(figsize=(10,2))
sns.boxplot(df1['Temperature (C)'])

plt.figure(figsize=(10,2))
sns.boxplot(df1['Apparent Temperature (C)'])

plt.figure(figsize=(10,2))
sns.boxplot(df1['Humidity'])

plt.figure(figsize=(10,2))
sns.boxplot(df1['Wind Speed (km/h)'])

plt.figure(figsize=(10,2))
sns.boxplot(df1['Wind Bearing (degrees)'])

plt.figure(figsize=(10,2))
sns.boxplot(df1['Visibility (km)'])

plt.figure(figsize=(10,2))
sns.boxplot(df1['Pressure (millibars)'])

Ако трябва да визуализирате всички диаграми на кутии за всяка колона, можете да следвате метода по-долу.

import warnings
warnings.filterwarnings("ignore")
plt.figure(figsize=(12,7))
plt.subplot(4,2,1)
plt.tight_layout(h_pad=6)
sns.boxplot(df1['Temperature (C)'])
plt.subplot(4,2,2)
sns.boxplot(df1['Apparent Temperature (C)'])
plt.subplot(4,2,3)
sns.boxplot(df1['Humidity'])
plt.subplot(4,2,4)
sns.boxplot(df1['Wind Speed (km/h)'])
plt.subplot(4,2,5)
sns.boxplot(df1['Wind Bearing (degrees)'])
plt.subplot(4,2,6)
sns.boxplot(df1['Visibility (km)'])
plt.subplot(4,2,7)
sns.boxplot(df1['Pressure (millibars)'])

От горните цифри можем да установим, че колоните като „Влажност“, „Скорост на вятъра (km/h)“, „Налягане (милибари)“ имат отклонения.

Следващата стъпка е да премахнете тези отклонения. За идентифициране на извънредни стойности 0,05 и 0,95 са избрани като минимален и максимален квантил.

min_value = df1.quantile(0.05)
max_value = df1.quantile(0.95)

Следващата стъпка е да изберете точки от данни, които са в рамките на минималния и максималния квантилен диапазон. Кодовите сегменти по-долу показват как да се покаже формата на кадрите с данни, която включва всички извънредни точки с данни, които трябва да бъдат премахнати във всяка колона.

eligible_outliers_humidity = df1[(df1['Humidity']<min_value['Humidity']) | (df1['Humidity']>max_value['Humidity'])].shape
eligible_outliers_windspeed = df1[(df1['Wind Speed (km/h)']<min_value['Wind Speed (km/h)']) | (df1['Wind Speed (km/h)']>max_value['Wind Speed (km/h)'])].shape
eligible_outliers_pressure = df1[(df1['Pressure (millibars)']<min_value['Pressure (millibars)']) | (df1['Pressure (millibars)']>max_value['Pressure (millibars)'])].shape
print(f"shape of the dataframes which includes all outliers need to be removed in features --> Humidity : {eligible_outliers_humidity} Wind Speed : {eligible_outliers_windspeed} Pressure : {eligible_outliers_pressure}")

Следващата стъпка е премахване на отклоненията. Извънредните стойности ще бъдат премахнати от df1 рамката с данни, както е показано по-долу.

df_without_outliers = df1[(df1['Wind Speed (km/h)']>min_value['Wind Speed (km/h)']) & (df1['Wind Speed (km/h)']<max_value['Wind Speed (km/h)']) & (df1['Humidity']>min_value['Humidity']) & (df1['Humidity']<max_value['Humidity'])& (df1['Pressure (millibars)']>min_value['Pressure (millibars)']) & (df1['Pressure (millibars)']<max_value['Pressure (millibars)'])]

Нека проверим формата на рамката с данни, след като премахнем отклоненията.

print(f"shape of the dataframe before removing outliers : {df1.shape}")
print(f"shaped of the dataframe after removing outliers : {df_without_outliers.shape}")

Сега нека проверим дали извънредните стойности са премахнати след повторното изчертаване на квадратните графики.

import warnings
warnings.filterwarnings("ignore")
plt.figure(figsize=(12,7))
plt.subplot(4,2,1)
plt.tight_layout(h_pad=6)
sns.boxplot(df_without_outliers['Temperature (C)'])
plt.subplot(4,2,2)
sns.boxplot(df_without_outliers['Apparent Temperature (C)'])
plt.subplot(4,2,3)
sns.boxplot(df_without_outliers['Humidity'])
plt.subplot(4,2,4)
sns.boxplot(df_without_outliers['Wind Speed (km/h)'])
plt.subplot(4,2,5)
sns.boxplot(df_without_outliers['Wind Bearing (degrees)'])
plt.subplot(4,2,6)
sns.boxplot(df_without_outliers['Visibility (km)'])
plt.subplot(4,2,7)
sns.boxplot(df_without_outliers['Pressure (millibars)'])

Обработка на категорични данни

Кодиране на данни за категориални променливи

Преди да обучим модела с помощта на набора от данни, трябва да кодираме категорични данни. Това е така, защото не можем да въвеждаме категории в модела такива, каквито са, освен ако не ги конвертираме в цифров формат.

В стъпката на кодиране на данни има две функции, които трябва да бъдат кодирани. Това са „Резюме“, „Тип утайка“.

Като първа стъпка типът данни на горепосочените функции трябва да бъде променен като „категория“. За това бях създал функция, наречена change_dtype(), както следва.

def change_dtype(before_coding):
    before_coding['Summary'] = before_coding['Summary'].astype('category')
    before_coding['Precip Type'] = before_coding['Precip Type'].astype('category')

След това извикайте функцията и променете типовете данни.

change_dtype(df_without_outliers)

Нека проверим колко различни стойности са налични във всяка от гореспоменатите колони.

distinct_values_precip = len(df_without_outliers['Precip Type'].value_counts())
distinct_values_summary = len(df_without_outliers['Summary'].value_counts())
print(f"distinct values of 'Precip Type' : {distinct_values_precip} \ndistinct values of 'Summary' : {distinct_values_summary} \n")

Бях приложил фиктивни стойности за двете колони „Обобщение“ и „Тип валеж“.

def encode_features(before_coding):
    encoded_df_1 = pd.get_dummies(before_coding['Summary'])
    encoded1 = pd.concat([before_coding,encoded_df_1],axis=1)
    encoded_df_2 = pd.get_dummies(before_coding['Precip Type'])
    encoded2 = pd.concat([before_coding,encoded_df_2],axis=1)
    df_new=pd.concat([encoded1,encoded2.iloc[:,-2:]],axis=1)
    coded_df = df_new.drop(['Formatted Date','Summary','Precip     Type'],axis=1)
    return coded_df

Извикайте горната функция и след това проверете дали всички категориални характеристики са кодирани.

df_without_outliers= encode_features(df_without_outliers)
df_without_outliers.head()

Разделяне на набор от данни за обучение и тестване

Преди да разделим набора от данни, първо трябва да изберем X с всички независими променливи и y със зависима променлива. „Привидната температура“ е зависимата променлива, а останалите всички променливи са независими променливи. Защото се опитваме да предскажем видимата температура за дадена влажност.

X=pd.concat([df_without_outliers.iloc[:,0],df_without_outliers.iloc[:,2:]],axis=1)
y=df_without_outliers.iloc[:,1]

Нека сега да разгледаме структурата на кадрите с данни X и y.

X.head()

y.head()

Нека разделим набора от данни, за да обучим и тестваме части с помощта на библиотеката scikit-learn.

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=20)

Трансформации на данни

Обикновено наборите от данни се състоят от изкривени разпределения, по-скоро нормални разпределения. Въпреки това, за обучение на модели, данните, идващи от нормални разпределения, са за предпочитане. От този етап прилагаме трансформации, за да преобразуваме изкривеното разпределение в нормално разпределение.

Q-Q партиди и хистограми се използват, за да се провери дали характеристиките следват нормално разпределение.

Нека приложим трансформации към X_train dataframe и X_test dataframe поотделно.

Нека проверим разпределението на всяка функция в X_train и X_test спрямо нормалното разпределение. След това можем да идентифицираме отклонените характеристики, като сравним разпределението с нормалното разпределение. Нека начертаем Q-Q диаграми за всяка характеристика.

def display_probplots(df):
    df_without_outliers=df
    plt.figure(figsize=(15,7))
    plt.tight_layout(h_pad=6)
    ax2=plt.subplot(4,2,1)
    stats.probplot(df_without_outliers['Temperature (C)'],  dist="norm", plot=plt)
    ax2.set_title("Apparent Temperature (C)")
    ax3=plt.subplot(4,2,2)
    stats.probplot(df_without_outliers['Humidity'], dist="norm", plot=plt)
    ax3.set_title("Humidity")
    ax4=plt.subplot(4,2,3)
    stats.probplot(df_without_outliers['Wind Speed (km/h)'],    dist="norm", plot=plt)
    ax4.set_title('Wind Speed (km/h)')
    ax5=plt.subplot(4,2,4)
    stats.probplot(df_without_outliers['Wind Bearing (degrees)'],   dist="norm", plot=plt)
    ax5.set_title('Wind Bearing (degrees)')
    ax6=plt.subplot(4,2,5)
    stats.probplot(df_without_outliers['Visibility (km)'], dist="norm", plot=plt)
    ax6.set_title('Visibility (km)')
    ax7=plt.subplot(4,2,6)
    stats.probplot(df_without_outliers['Pressure (millibars)'], dist="norm", plot=plt)
    ax7.set_title('Pressure (millibars)')

След това извикайте горната функция display_probplots(), за да изчертаете графики на вероятностите.

display_probplots(X_train)

display_probplots(X_test)

Също така, нека начертаем и хистограми.

X_train.iloc[:,:6].hist()

X_test.iloc[:,:6].hist()

От горните хистограми можем да установим, че функцията „Влажност“ има ляво изкривено разпределение, а функцията „Скорост на вятъра“ има изкривено дясно разпределение.

Нека създадем функция за трансформиране на характеристиката на „Влажност“ чрез прилагане на експоненциалната трансформация.

from sklearn.preprocessing import FunctionTransformer
def exp_transformer(df):
    df_without_outliers=df
    exp_transformer = FunctionTransformer(np.exp)
    
    transformed_humidity = exp_transformer.transform(df_without_outliers[['Humidity']])
    df_without_outliers['Humidity']=transformed_humidity['Humidity']
    return df_without_outliers

Нека извикаме функцията exp_transformer(), за да трансформираме ляво изкривено разпределение в нормално разпределение.

X_trained_transformed = exp_transformer(X_train)
X_trained_transformed.iloc[:,:6].hist()

X_test_transformed = exp_transformer(X_test)
X_test_transformed.iloc[:,:6].hist()

Втората трансформация е свързана с характеристиката на скоростта на вятъра чрез прилагане на лог трансформацията.

from sklearn.preprocessing import FunctionTransformer
def log_transformer(df):
    df_without_outliers=df
    logarithm_transformer = FunctionTransformer(np.log1p)
    transformed_windspeed = logarithm_transformer.transform(df_without_outliers['Wind Speed (km/h)'])
    df_without_outliers['Wind Speed (km/h)']=transformed_windspeed
    return df_without_outliers

Нека извикаме функцията log_transformer(), за да преобразуваме дясното изкривено разпределение.

X_trained_transformed=log_transformer(X_train)
X_trained_transformed.iloc[:,:6].hist()

X_test_transformed=log_transformer(X_test)
X_test_transformed.iloc[:,:6].hist()

Сега нека проучим функциите на y_train и y_test, за да проверим дали имат изкривени разпределения.

Бях създал функция за показване на вероятностните графики чрез предаване на тестови разделяния към функцията.

def display_probplots_y(df):
    df_without_outliers=df
    plt.figure(figsize=(15,7))
    plt.tight_layout(h_pad=6)
    ax2=plt.subplot(4,2,1)
    stats.probplot(df_without_outliers, dist="norm", plot=plt)
    ax2.set_title("Apparent Temperature (C)")

Тогава нека извикаме функцията, като предадем рамки с данни y_train и y_test.

display_probplots_y(y_train)

display_probplots_y(y_test)

Нека начертаем и хистограмите на кадрите с данни y_train и y_test.

y_train.hist()

y_test.hist()

Като наблюдаваме горните фигури, можем да решим, че кадрите с данни y_train и y_test не се изискват за трансформиране.

Мащабиране на функции

При мащабирането на характеристиките ние нормализираме диапазона от стойности на променливите.

В този пример използвам стандартизация и трябва да внимаваме да прилагаме стандартизация само към цифровите характеристики, с изключение на кодирани характеристики, както следва.

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
scaler = StandardScaler()
standarized_np_X_train = X_trained_transformed.values
standarized_np_X_train[:,:6] = scaler.fit_transform(X_trained_transformed.iloc[:,:6])
standarized_np_X_test = X_test_transformed.values
standarized_np_X_test[:,:6] = scaler.transform(X_test_transformed.iloc[:,:6])
standarized_np_y_train = scaler.fit_transform(y_train.values.reshape(-1,1))
standarized_np_y_test = scaler.transform(y_test.values.reshape(-1,1))

Нека покажем резултата от мащабирането на функцията чрез преобразуване на масива numpy в рамка с данни.

normalised_df_X_train=pd.DataFrame(standarized_np_X_train,columns=X_trained_transformed.columns)
normalised_df_X_test=pd.DataFrame(standarized_np_X_test,columns=X_test_transformed.columns)
normalised_df_y_train=pd.DataFrame(standarized_np_y_train,columns=['Apparent Temperature (C)'])
normalised_df_y_test=pd.DataFrame(standarized_np_y_test,columns=['Apparent Temperature (C)'])
normalised_df_X_train.head()

normalised_df_X_test.head()

normalised_df_y_train.head()

normalised_df_y_test.head()

Идентифициране на значими и независими характеристики

Като следваща стъпка, нека разберем дали характеристиките на X са независими или не, като начертаем топлинна карта.

import seaborn as sns
plt.figure(figsize=(12,7))
sns.heatmap(X.iloc[:,:6].corr(),annot=True);

Можем да установим, че характеристиките не са силно свързани с други характеристики. Така че можем да кажем, че характеристиките са независими. Ако има наличен набор от силно корелирани функции, една функция от този набор от силно корелирани функции може да бъде премахната. Тъй като една характеристика може да бъде производна променлива на друга. Въпреки това, преди да премахнем функция, трябва да сме добре запознати с набора от данни. Въпреки че може да има висока корелация между променливите, може да се окаже, че една характеристика не може да бъде извлечена от другата.

След това открийте значимите характеристики, като сравните корелацията между всяка характеристика и зависимата променлива.

plt.figure(figsize=(12,7))
x_data = X.iloc[:,:6].copy()
x_data['Apparent Temperature'] = y
print(x_data.corr())
sns.heatmap(x_data.corr(),annot=True)

От горната топлинна карта можем да видим, че характеристики като „температура“, „Влажност“, „Видимост“ са значителни. Тъй като те имат висока корелация със зависимата променлива („Привидна температура“).

Но този подход не е добър за огромни масиви от данни. Тъй като трябва ръчно да преминем през всяка стойност на корелация, за да изберем важни характеристики. Това е досадна задача. Не се притеснявайте, имаме невероятна техника, наречена „Анализ на основните компоненти (PCA)“.

Прилагане на принципен анализ на компонентите (PCA)

Опитвам се да обуча модел, като използвам и техники за намаляване на размерността.

Първо приложете PCA, за да извлечете функции.

from sklearn.decomposition import PCA
pca = PCA()
X_pca = pca.fit_transform(normalised_df_X_train)
explained_variance=pca.explained_variance_ratio_
explained_variance

Създадох функция, за да получа броя на значимите характеристики, които да бъдат избрани от горния масив с коефициент на вариация.

def count_dimension(arr1):
    print(arr1)
    sum=0
    count=0
    for i in arr1:
        sum+=i
        count +=1
        if(sum>=0.95):
            return count
    return -1
print(f"No of features needed to be extracted : {count_dimension(explained_variance)}")

Сега приложете PCA към normalised_df_X_train и normalised_df_X_test кадри с данни с посочения по-горе брой компоненти.

pca=PCA(n_components=8)
X_train_pca=pca.fit_transform(normalised_df_X_train)
X_test_pca=pca.transform(normalised_df_X_test)

Прилагане на модел на линейна регресия

Нека обучим модела на линейна регресия с помощта на X_train_pca.

from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
model= regressor.fit(X_train_pca,normalised_df_y_train)

След това използвайте функцията .predict(), за да предвидите стойността на y за дадени независими променливи в X_test_pca.

y_pred =regressor.predict(X_test_pca)

Оценяване на регресионния модел

За да оценя модела на линейна регресия, използвам следните мерки.

  • Средна квадратна грешка (MSE)
  • Средноквадратична грешка (RMSE)
  • Средна абсолютна грешка
from sklearn import metrics
accuracy = metrics.r2_score(normalised_df_y_test, y_pred)
accuracy

from math import sqrt
from sklearn.metrics import mean_squared_error
rmse = sqrt(mean_squared_error(normalised_df_y_test, y_pred))
rmse

from math import sqrt
mse = mean_squared_error(normalised_df_y_test, y_pred)
mse

Нека начертаем диаграма, за да визуализираме вариациите между действителните стойности и прогнозите на модела, както е показано по-долу.

plt.figure(figsize=(12,7))
finaldataframe =pd.DataFrame({'actual':normalised_df_y_test.values[:,0],'prediction':y_pred[:,0]})
finaldataframe2=finaldataframe.reset_index(drop=True)
plt.plot(finaldataframe2['prediction'][:150], label = "Pred")
plt.plot(finaldataframe2['actual'][:150], label = "Actual")
plt.xlabel('x - axis')
plt.ylabel('y - axis')
plt.title('Deviations between actual and predicted values.')
plt.legend()
plt.show()

Прилагане на модел на линейна регресия без PCA

Нека приложим модела на линейна регресия, без да използваме PCA и се опитаме да предвидим „видимата температура“ за дадени от потребителя стойности.

from sklearn.linear_model import LinearRegression
regressor1 = LinearRegression()
model= regressor1.fit(normalised_df_X_train.values,normalised_df_y_train.values) # independent feature 'Humidity' and dependent feature 'Apparent temperature'

Като следваща стъпка използвайте функцията .predict(), за да получите прогнозирани стойности на y за дадените стойности на функцията X_test.

y_pred1 =regressor1.predict(normalised_df_X_test.values)
y_pred1

Нека оценим модела, като вземем мерките за MSE, RMSE и r2, както следва.

from sklearn import metrics
accuracy = metrics.r2_score(normalised_df_y_test, y_pred1)
accuracy

from math import sqrt
from sklearn.metrics import mean_squared_error
rmse = sqrt(mean_squared_error(normalised_df_y_test, y_pred1))
rmse

from math import sqrt
mse = mean_squared_error(normalised_df_y_test, y_pred1)
mse

Когато разглеждаме математиката зад регресията, даденият модел се тълкува по следния начин.

β0 означава пресечната точка на линейната графика. β1, β2…β19означават коефициентите на линейната графика (включително и кодирани променливи).

Нека да видим пресечната точка и коефициентите, които са монтирани от модела.

regressor1.intercept_

regressor1.coef_

Сега нека се опитаме да предвидим „видимата температура“ за дадени от потребителя стойности. Можем да предскажем видимата температура, като дадем всички x стойности като масив numpy. Но от този модел не можем да предскажем видимата температура, просто предавайки стойността на влажността.

predicted_appearent_temp =  regressor1.predict(scaler.transform([[9,0.83,11,259,15.8263,1016.51,0,0,0,0,0,0,0,1,0,0,0,1,0]]))
predicted_appearent_temp

Заключение

Видимата температура е температурата, възприемана от хората.

В този урок можем ясно да видим кои са основните фактори, влияещи върху видимата температура.

  • Когато температурата се повиши, това води до повишаване на видимата температура. Това е така, защото температурата има висок положителен коефициент, даден от модела.
  • Когато влажността се увеличи, това също води до повишаване на видимата температура. Това е така, защото влажността има положителна, но ниска стойност на коефициента, дадена от модела в сравнение с температурата.
  • Когато скоростта на вятъра се увеличи, това води до намаляване на видимата температура. Това е така, защото скоростта на вятъра има отрицателна стойност на коефициента, дадена от модела.
  • Влажността и температурата са в обратна зависимост.

Бих искал да благодаря на д-р Субха Фернандо (старши преподавател в университета в Моратува) за това, че ме мотивира да започна пътуването си в писането на блогове за машинно обучение.

Благодаря ви, че прочетохте този урок и се надявам да получите малко знания за това как да приложите линейна регресия към избран набор от данни след предварителна обработка. Ако имате някакви съмнения, моля свържете се с мен.

Приятно практикуване… 👩‍💻