Введение

Как гласит индонезийская пословица: «приготовьте зонт до дождя», эта модель также пытается подготовить нас к предстоящей погоде.

В это неопределенное время изменения климата мы знаем, что погода сейчас непростая штука. Сегодня мы, возможно, вспотеем до мозга костей. Завтра? Мы можем одеваться слишком мало для того маленького солнца, которое у нас будет.

Поэтому прогноз температуры жизненно важен для того, чтобы мы могли лучше планировать свои дни.

Цель

Эта модель пытается предоставить пользователю три различных значения температуры: среднюю, минимальную и максимальную при определенных входных данных.

Набор данных

Эта модель использует данные Kaggle. Для более чистого набора данных используются стандартные методы очистки и предварительной обработки. Подробное объяснение приведено ниже.

Объединение и очистка:

Наборы данных climate и station объединяются, чтобы получить province_id для каждого наблюдения. После этого удаляем ненужные столбцы, а именно station_name, region_name, latitude, longitude и region_id..

# Merging data to show province_id for every observation in climate

dataset = pd.merge(climate, station, on='station_id', how='left')
dataset = dataset.drop(['station_name', 'region_name', 'latitude', 'longitude', 'region_id'], axis=1)
dataset.head()

Очистка

Преобразование date в дату и время и последующее извлечение из него month для последующего использования.

# Changing 'date' type into datetime

dataset['date'] = pd.to_datetime(dataset['date'], format='%d-%m-%Y')
dataset.info()

# Adding a column consisting month value with int type

dataset['month'] = dataset['date'].dt.month

# Drop date and station column
dataset = dataset.drop(['date', 'station_id'], axis=1)

Защита данных

Создайте функцию для обеспечения соответствующих входных данных.

# Check data types
def check_data(input_data, config):
    
    assert input_data.select_dtypes("int").columns.to_list() == config["int_columns"], "an error occurs in int column(s)."
    assert input_data.select_dtypes("float").columns.to_list() == config["float_columns"], "an error occurs in float column(s)."
    assert input_data.select_dtypes("object").columns.to_list() == config["object_columns"], "an error occurs in object column(s)."

Разделение

Разделите данные на входные и выходные, а затем на обучающий, действительный и тестовый набор данных.

x = dataset[config["input"]].copy()
y = dataset[config["output"]].copy()

# Splitting data into train and test

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state = 123)

# Splitting test data into validation and test

x_valid, x_test, y_valid, y_test = train_test_split(x_test, y_test, test_size = 0.2, random_state = 123)

Вменение

В train_data найдено несколько нулевых значений. Следовательно, нам нужно сделать некоторые вменения. Для столбцов с плавающей запятой вмененные значения являются медианными, а для столбцов с плавающей запятой — новой меткой «неизвестно».

# change non-float null into 'unknown' and float null into median for train data
def impute_train_missing(df):
    columns = list(df.columns.values)
    for column in columns:
        try:
            if df[column].dtypes.name == 'float64':
                median = df[column].median()
                df[column] = df[column].fillna(median)
                
            if df[column].dtypes.name == 'int64':
                df[column] = df[column].fillna('unknown')
                
            else:
                df[column] = df[column].fillna('unknown')
    
        except:
            pass

После завершения вменения train_data следующим шагом будет вменение valid_data и test_data. Это вменение выполняется с использованием train_data в качестве базы во избежание утечки данных.

# change non-float null into 'unknown' and float null into median for valid and test data using train data
def impute_valid_test_missing(train_df, imputed_df):
    columns = list(imputed_df.columns.values)
    for column in columns:
        try:
            if imputed_df[column].dtypes.name == 'float64':
                median = train_df[column].median()
                imputed_df[column] = imputed_df[column].fillna(median)
                
            if imputed_df[column].dtypes.name == 'int64':
                imputed_df[column] = imputed_df[column].fillna('unknown')
            
            else:
                imputed_df[column] = imputed_df[column].fillna('unknown')
    
        except:
            pass

Масштабирование

Масштабирование выполняется для всех наборов данных с использованием train_data в качестве базы.

train_data масштабирование:

def train_scaler(train_data, scaler = None):
    """
    Standardizing train data
    :param x: <pandas DataFrame> data
    :param scaler: <sklearn object> scaler, default None
    :return x_scaled: <pandas Dataframe> standardized data
    :param scaler: <sklearn object> scaler, default None
    """
    if scaler != None:
        pass
    else:
        # Buat & fit encoder
        scaler = StandardScaler()
        scaler.fit(train_data)

    # Tranform data
    train_data_scaled = scaler.transform(train_data)
    train_data_scaled = pd.DataFrame(train_data_scaled,
                            columns = train_data.columns,
                            index = train_data.index)
    
    return train_data_scaled, scaler

# standardizing numerical columns in train data using StandardScaler

train_set_clean[numerical_cols], scaler = train_scaler(train_data = train_set_clean[numerical_cols])
train_set_clean.head()

valid_data и test_data:

def valid_test_scaler(data, scaler):  
    # Standardizing test data using train data parameter
    test_data_scaled = scaler.transform(data)
    test_data_scaled = pd.DataFrame(test_data_scaled, columns = data.columns, index = data.index)
    
    
    return test_data_scaled

# standardizing numerical columns in valid data using StandardScaler

valid_set_clean[numerical_cols] = valid_test_scaler(data = valid_set_clean[numerical_cols], scaler=scaler)
valid_set_clean.head()

# standardizing numerical columns in test data using StandardScaler

test_set_clean[numerical_cols] = valid_test_scaler(data = test_set_clean[numerical_cols], scaler=scaler)
test_set_clean.head()

Метод

Алгоритм

Эта модель использует усиление градиента.

Метрики

В этой модели в качестве показателей используется среднеквадратическая ошибка (MSE).

Результаты

Как указано выше в разделе «Цель», эта модель пытается найти три значения, а именно среднюю, минимальную и максимальную температуру. Поэтому все действия ниже выполняются для каждого из них. В демонстрационных целях последующие описания записываются, чтобы показать результаты для Tn, который является столбцом для минимальной температуры.

Базовый уровень

В качестве базового уровня в этой модели используется среднее значение от train_data.

Тренироваться

Используя GradientBoostingRegressor, эта модель вписывается как в данные train, так и в test.

grad_tree_Tn = GradientBoostingRegressor(random_state=123)
grad_tree_Tn.fit(x_train_clean, y_train_clean['Tn'])

y_pred_train_Tn = grad_tree_Tn.predict(x_train_clean)
y_pred_test_Tn = grad_tree_Tn.predict(x_test_clean)

mse_train_Tn = mean_squared_error(y_pred_train_Tn, y_train_clean['Tn'])
mse_test_Tn = mean_squared_error(y_pred_test_Tn, y_test_clean['Tn'])

mse_train_Tn, mse_test_Tn

Перекрестная проверка

После получения результатов вышеуказанного процесса модель продолжает выполнять перекрестную проверку, чтобы получить наилучшие параметры. Этот процесс использует valid_data, который сам был отделен от train_data..

# Create GridSearch for Tn using such parameters as stated in config file
gb_tree_Tn_valid = GradientBoostingRegressor(random_state = 123)

# Run GridSearch
gb_tree_Tn_cv = GridSearchCV(estimator = gb_tree_Tn_valid,
                          param_grid = config['params'],
                          cv = 5,
                          scoring = "neg_mean_squared_error")

# Fit the model using validation data
gb_tree_Tn_cv.fit(x_valid_clean, y_valid_clean['Tn'])

# Find the best params
gb_tree_Tn_cv.best_params_

# Find the best score
gb_tree_Tn_cv.best_score_

Количество складок равно 5, и причиной этого являются просто лучшие практики и ограничения по времени. Использование большего количества данных может дать более точные параметры, но все же время обработки является ограничением.

Тест

После получения лучших параметров в процессе перекрестной проверки, описанном выше, модель соответствует train_data, используя эти значения.

# Refitting a random forest for Tn using the best parameters
gb_tree_Tn_final = GradientBoostingRegressor(n_estimators = gb_tree_Tn_cv.best_params_['n_estimators'],
                                learning_rate = gb_tree_Tn_cv.best_params_["learning_rate"],
                                random_state = 123)

gb_tree_Tn_final.fit(x_train_clean, y_train_clean['Tn'])

Выход

Наконец, модель прогнозирует, используя данные train и test.

# Predict
y_pred_train_Tn_final = gb_tree_Tn_final.predict(x_train_clean)
y_pred_test_Tn_final = gb_tree_Tn_final.predict(x_test_clean)

# MSE
mse_train_Tn_final = mean_squared_error(y_train_clean['Tn'], y_pred_train_Tn_final)
mse_test_Tn_final = mean_squared_error(y_test_clean['Tn'], y_pred_test_Tn_final)

mse_train_Tn_final, mse_test_Tn_final

При использовании этой модели результаты следующие:

|         | MSE Baseline | MSE Train | MSE Test | (Test/Baseline) - 1 |
| --------|:------------:| :--------:| :-------:| :------------------:|
| Minumum | 4.924533     | 2.990025  | 3.049104 | 38,21%              |
| Maximum | 5.148906     | 2.765183  | 2.328772 | 53,85%              |
| Average | 3.466188     | 1.620189  | 1.659483 | 52,31%              |

Заключение и дальнейшая работа

Используя повышение градиента с MSE в качестве метрики, было обнаружено, что модель работает значительно лучше, чем базовый уровень, особенно для максимальной и средней температуры.

Несмотря на это, существует тревожное представление о минимальной температуре, при которой тестовая MSE довольно далека от двух других. Должна быть проведена дальнейшая работа по выявлению причин этого.

Репо доступно здесь.