Недавно я играл с машинным обучением на различных облачных платформах, таких как AWS, Google и Azure. В этой статье мы рассмотрим популярное соревнование Kaggle по предсказанию выживания титанических пассажиров. Мы будем использовать SageMaker для решения этой задачи и в процессе изучим основы платформы SageMaker.

Для начала загрузим данные обучения с сайта Kaggle https://www.kaggle.com/c/titanic/data. У него есть два файла, а именно train.csv, содержащий данные обучения, и test.csv для окончательных прогнозов.

Исследовательский анализ данных

Давайте загрузим данные с помощью Pandas и распечатаем пропущенные значения.

train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')
print(train_data.isnull().sum())
print(train_data.head())
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64
Name     Sex   Age  SibSp  \
0                            Braund, Mr. Owen Harris    male  22.0      1   
1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1   
2                             Heikkinen, Miss. Laina  female  26.0      0   
3       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1   
4                           Allen, Mr. William Henry    male  35.0      0   
   Parch            Ticket     Fare Cabin Embarked  
0      0         A/5 21171   7.2500   NaN        S  
1      0          PC 17599  71.2833   C85        C  
2      0  STON/O2. 3101282   7.9250   NaN        S  
3      0            113803  53.1000  C123        S  
4      0            373450   8.0500   NaN        S

Мы видим, что есть несколько пропущенных значений для возраста, каюты и посадки. Также обратите внимание, что возраст и стоимость проезда - это значения с плавающей запятой, которые нам нужно преобразовать в целые числа. Для Cabin он начинается с категории, за которой следует номер, поэтому нам нужно избавиться от конечных чисел. Есть несколько функций, которые не имеют отношения к выводу, например имя, идентификатор пассажира и билет, которые мы можем просто отбросить. Теперь, когда у нас есть базовое представление о данных, давайте посмотрим, как мы можем теперь выполнить проектирование функций.

Разработка функций

Для начала избавимся от ненужных функций.

# remove irrelevant feature data
train_data.drop(labels=['Name', 'PassengerId', 'Ticket'], axis=1, inplace=True)

В embarked всего 2 недостающих записи, лучше отбросить эти строки, а не заполнять их, так как это не окажет значительного влияния на вывод.

# drop NaN rows for embarked
train_data = train_data.dropna(subset=['Embarked'])

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

# replace age NaN with mean age
mean_age = train_data['Age'].mean()
train_data['Age'] = train_data['Age'].fillna(mean_age)
# round age
train_data['Age'] = train_data['Age'].apply(lambda x : int(x))

Для номера каюты оставим номер и сохраним только категорию. Кроме того, отсутствующие значения заменяются самой большой категорией в наборе данных, которая называется "C".

# extract cabin category
train_data['Cabin'] = train_data['Cabin'].apply(lambda x : str(x)[0])
# replace cabin NaN with maximum 'S'
train_data['Cabin'] = train_data['Cabin'].apply(lambda x : 'C' if x == 'n' else x)

Округлить тариф до ближайшего целого числа для обработки.

# round fare
train_data['Fare'] = train_data['Fare'].apply(lambda x : int(x))

Возраст, каюта и место посадки - это буквенно-цифровые значения, поэтому нам нужно применить кодировку метки для преобразования в целые числа.

# label encode sex, cabin and embarked
label_encoder = LabelEncoder()
train_data['Sex'] = label_encoder.fit_transform(train_data['Sex'])
train_data['Cabin'] = label_encoder.fit_transform(train_data['Cabin'])
train_data['Embarked'] = label_encoder.fit_transform(train_data['Embarked'])

Еще одна вещь, которую нам нужно сделать перед сохранением данных, - это переместить целевой столбец на первое место, поскольку это требуется для алгоритма SageMaker XGBoost.

# save and them drop the column
survived = train_data['Survived']
train_data.drop(labels=['Survived'], axis=1, inplace=True)
# insert target as first column
train_data.insert(0, 'Survived', survived)

Наконец, разделите набор данных на обучение, проверку и тестирование и сохраните локально в разных файлах CSV.

train_xgboost, validation_xgboost, test_xgboost = np.split(train_data.sample(frac=1, random_state=1729), [int(0.7 * len(train_data)), int(0.9 * len(train_data))])
# remove header as it is not required by XGBoost
train_xgboost.to_csv('train_xgboost.csv', header=False, index=False)
validation_xgboost.to_csv('validation_xgboost.csv', header=False, index=False)
test_xgboost.to_csv('test_xgboost.csv', header=False, index=False)

Вот и все, теперь мы готовы пройти обучение модели.

Обучение модели

Прежде чем мы начнем, убедитесь, что учетные данные AWS настроены с соответствующими ролями пользователей, которые разрешают доступ к SageMaker и S3.

cat ~/.aws/credentials
[default]
aws_access_key_id = <your access key id here>  
aws_secret_access_key = <your secret access key here>

Создайте сеанс SageMaker и загрузите обучающие, проверочные и тестовые CSV-файлы в S3.

sagemaker_session = sagemaker.Session()
train_path = sagemaker_session.upload_data(path='train_xgboost.csv', key_prefix='dataset')
validation_path = sagemaker_session.upload_data(path='validation_xgboost.csv', key_prefix='dataset')
test_path = sagemaker_session.upload_data(path='test.csv', key_prefix='test')

Получите образ контейнера XGBoost.

container = get_image_uri(boto3.Session().region_name, 'xgboost', repo_version='0.90-1')

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

s3_input_train = sagemaker.s3_input(s3_data=train_path, content_type='csv')
s3_input_validation = sagemaker.s3_input(s3_data=validation_path, content_type='csv')

Создайте оценщик XGBoost и установите гиперпараметры. Мы используем спотовый инстанс AWS, чтобы сэкономить деньги. Замените роль той, которую вы создали в начале. Также обратите внимание на сегмент и префикс, который используется для указания выходного сегмента для созданной модели.

bucket = 'sagemaker-us-east-1-756448110530'
prefix = 'dataset'
estimator = sagemaker.estimator.Estimator(
container,
'AmazonSageMaker-ExecutionRole-20190815T111389',
train_instance_count=1,
                                                   train_instance_type='ml.m4.2xlarge',
                                          output_path='s3://{}/{}/output'.format(bucket, prefix),
                                          sagemaker_session=sagemaker_session,
                                            train_use_spot_instances=True,
train_max_run=120,
train_max_wait=180,
)
estimator.set_hyperparameters(eta=0.1,
                             objective='binary:logistic',
                             num_round=25,
                             eval_metric='accuracy')

И, наконец, обучите модель.

estimator.fit({
    'train': s3_input_train,
    'validation': s3_input_validation
})

Посмотрите в журнал на предмет достигнутой точности и вашей экономии с помощью спотового экземпляра. Здесь мы достигли точности проверки 84%, что не идеально, но подходит для этого руководства по демонстрации SageMaker.

[24]#011train-error:0.101286#011validation-error:0.157303#011train-accuracy:0.898714#011validation-accuracy:0.842697
Training seconds: 47
Billable seconds: 17
Managed Spot Training savings: 63.8%

После успешного завершения обучения модель сохраняется в s3: // ‹имя вашего сегмента› / dataset / output / sagemaker-xgboost- ‹отметка времени› / output / model.tar.gz. Это сжатый файл модели с маринованным питоном. В следующем разделе мы увидим, как мы можем использовать эту модель для прогнозирования на локальном компьютере.

Прогноз

Начните с распаковки модели и загрузите ее с помощью pickle. Убедитесь, что вы используете версию XGBoost 0.90 на момент написания этой статьи, которая поддерживается SageMaker. В случае несовпадения версий загрузка модели может завершиться ошибкой.

model = pickle.loads(open('xgboost-model', 'rb').read())

Помните, что у нас есть тестовые данные, сохраненные в файле test.csv. Нам нужно загрузить этот файл и применить те же шаги по проектированию функций, которые мы выполнили выше. В рамках этого урока позвольте мне повторить здесь те же шаги.

# remove irrelevant feature data
test_data.drop(labels=['Name', 'Ticket'], axis=1, inplace=True)
# extract cabin number
test_data['Cabin'] = test_data['Cabin'].apply(lambda x : str(x)[0])
# replace age NaN with mean age
mean_age = test_data['Age'].mean()
test_data['Age'] = test_data['Age'].fillna(mean_age)
# replace fare NaN with mean fare
mean_fare = test_data['Fare'].mean()
test_data['Fare'] = test_data['Fare'].fillna(mean_age)
# round age
test_data['Age'] = test_data['Age'].apply(lambda x : int(x))
# round fare
test_data['Fare'] = test_data['Fare'].apply(lambda x : int(x))
# label encode sex, cabin and embarked
label_encoder = LabelEncoder()
test_data['Sex'] = label_encoder.fit_transform(test_data['Sex'])
test_data['Cabin'] = label_encoder.fit_transform(test_data['Cabin'])
test_data['Embarked'] = label_encoder.fit_transform(test_data['Embarked'])

Для прогнозирования нам нужны входные данные в определенном формате, который XGBoost ожидает, следовательно, скрывает.

test_X = test_data.as_matrix(columns=['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Cabin', 'Embarked'])
test_X = xgboost.DMatrix(test_X)

А теперь пора делать прогнозы.

predictions = model.predict(test_X)

Если вы напечатаете прогноз на этом этапе, это будет массив с числами вероятности, которые нам нужно преобразовать либо в 1 для выживших, либо в 0 для мертвых. Мы будем использовать 0,7 в качестве порога, чтобы решить это.

def transform(val):
    if val > 0.7:
        return 1
    else:
        return 0
        
predictions = list(map(transform, predictions))

Наконец, создайте файл отправки, который вы можете загрузить в Kaggle.

submission = pd.DataFrame({ 'PassengerId': test_data['PassengerId'],
                            'Survived': predictions })
submission.to_csv("submission.csv", index=False)

Отлично, вот и все,

Надеюсь, у вас есть базовый рабочий процесс использования AWS SageMaker для задачи классификации. Чтобы увидеть полный исходный код, посетите мой сайт на github https://github.com/mngaonkar/kaggle-titanic и не стесняйтесь повторно использовать код.

Продолжайте кодировать!