В последнем посте, который я написал о Натане и моих попытках решить Титаническую проблему Каггла, я упомянул, что наш следующий шаг должен был попробовать научиться научению, поэтому я подумал, что мне следует кратко изложить , к чему мы пришли.
Нам нужно было написать алгоритм классификации, чтобы определить, выжил ли человек на борту «Титаника», и, к счастью, у scikit-learn есть обширная документация по каждому из этих алгоритмов .
К сожалению, почти во всех этих примерах используются сложные структуры данных, и мы загрузили данные, используя панд, и не хотели переключаться обратно!
К счастью, было очень легко перевести данные в пустой формат, вызвав «значения» в структуре данных pandas, что мы узнали из ответа о переполнении стека .
Например, если бы мы подключили ExtraTreesClassifier, который определял выживаемость на основе атрибутов ‘Fare’ и ‘Pclass’, мы могли бы написать следующий код:
import pandas as pd from sklearn.ensemble import ExtraTreesClassifier from sklearn.cross_validation import cross_val_score train_df = pd.read_csv("train.csv") et = ExtraTreesClassifier(n_estimators=100, max_depth=None, min_samples_split=1, random_state=0) columns = ["Fare", "Pclass"] labels = train_df["Survived"].values features = train_df[list(columns)].values et_score = cross_val_score(et, features, labels, n_jobs=-1).mean() print("{0} -> ET: {1})".format(columns, et_score))
Начнем с чтения в файле CSV, который выглядит следующим образом:
$ head -n5 train.csv PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked 1,0,3,"Braund, Mr. Owen Harris",male,22,1,0,A/5 21171,7.25,,S 2,1,1,"Cumings, Mrs. John Bradley (Florence Briggs Thayer)",female,38,1,0,PC 17599,71.2833,C85,C 3,1,3,"Heikkinen, Miss. Laina",female,26,0,0,STON/O2. 3101282,7.925,,S 4,1,1,"Futrelle, Mrs. Jacques Heath (Lily May Peel)",female,35,1,0,113803,53.1,C123,S
Затем мы создаем наш классификатор, который « соответствует ряду рандомизированных деревьев решений (также называемых дополнительными деревьями) на различных подвыборках набора данных и используем усреднение для повышения точности прогнозирования и контроля соответствия. «То есть лучшая версия случайного леса .
На следующей строке мы опишем функции, которые мы хотим использовать классификатором, затем мы конвертируем метки и объекты в пустой формат, чтобы мы могли передать его классификатору.
Наконец, мы вызываем функцию cross_val_score, которая разбивает наш обучающий набор данных на обучающие и тестовые компоненты, обучает классификатор по первому и проверяет его точность, используя последний.
Если мы запустим этот код, мы получим примерно следующий вывод:
$ python et.py ['Fare', 'Pclass'] -> ET: 0.687991021324)
Это на самом деле хуже, чем мы могли бы сказать, что женщины выжили, а мужчины нет.
Мы можем ввести «Секс» в классификатор, добавив его в список столбцов:
columns = ["Fare", "Pclass", "Sex"]
Если мы повторно запустим код, мы получим следующую ошибку:
$ python et.py An unexpected error occurred while tokenizing input The following traceback may be corrupted or invalid The error message is: ('EOF in multi-line statement', (514, 0)) ... Traceback (most recent call last): File "et.py", line 14, in <module> et_score = cross_val_score(et, features, labels, n_jobs=-1).mean() File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/cross_validation.py", line 1152, in cross_val_score for train, test in cv) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/externals/joblib/parallel.py", line 519, in __call__ self.retrieve() File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/externals/joblib/parallel.py", line 450, in retrieve raise exception_type(report) sklearn.externals.joblib.my_exceptions.JoblibValueError/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/externals/joblib/my_exceptions.py:26: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6 self.message, : JoblibValueError ___________________________________________________________________________ Multiprocessing exception: ... ValueError: could not convert string to float: male ___________________________________________________________________________
Это немного многословный способ сказать нам, что мы не можем передавать нечисловые признаки в классификатор — в этом случае «Секс» имеет значения «женский» и «мужской». Нам нужно написать функцию для замены этих значений числовыми эквивалентами.
train_df["Sex"] = train_df["Sex"].apply(lambda sex: 0 if sex == "male" else 1)
Теперь, если мы повторно запустим классификатор, мы получим немного более точный прогноз:
$ python et.py ['Fare', 'Pclass', 'Sex'] -> ET: 0.813692480359)
Следующим шагом является использование классификатора для набора тестовых данных, поэтому давайте загрузим данные и выполним прогноз:
test_df = pd.read_csv("test.csv") et.fit(features, labels) et.predict(test_df[columns].values)
Теперь, если мы запустим это:
$ python et.py ['Fare', 'Pclass', 'Sex'] -> ET: 0.813692480359) Traceback (most recent call last): File "et.py", line 22, in <module> et.predict(test_df[columns].values) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/ensemble/forest.py", line 444, in predict proba = self.predict_proba(X) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/ensemble/forest.py", line 479, in predict_proba X = array2d(X, dtype=DTYPE) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/utils/validation.py", line 91, in array2d X_2d = np.asarray(np.atleast_2d(X), dtype=dtype, order=order) File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy/core/numeric.py", line 235, in asarray return array(a, dtype, copy=False, order=order) ValueError: could not convert string to float: male
это та же проблема, что у нас была раньше! Нам также необходимо заменить значения «мужской» и «женский» в тестовом наборе, поэтому сейчас мы вытащим функцию для этого.
def replace_non_numeric(df): df["Sex"] = df["Sex"].apply(lambda sex: 0 if sex == "male" else 1) return df
Теперь мы вызовем эту функцию с нашими фреймами данных обучения и тестирования:
train_df = replace_non_numeric(pd.read_csv("train.csv")) ... test_df = replace_non_numeric(pd.read_csv("test.csv"))
Если мы запустим программу снова:
$ python et.py ['Fare', 'Pclass', 'Sex'] -> ET: 0.813692480359) Traceback (most recent call last): File "et.py", line 26, in <module> et.predict(test_df[columns].values) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/ensemble/forest.py", line 444, in predict proba = self.predict_proba(X) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/ensemble/forest.py", line 479, in predict_proba X = array2d(X, dtype=DTYPE) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/utils/validation.py", line 93, in array2d _assert_all_finite(X_2d) File "/Library/Python/2.7/site-packages/scikit_learn-0.14.1-py2.7-macosx-10.8-intel.egg/sklearn/utils/validation.py", line 27, in _assert_all_finite raise ValueError("Array contains NaN or infinity.") ValueError: Array contains NaN or infinity.
В тестовом наборе отсутствуют значения, поэтому мы заменим их на средние значения из нашего обучающего набора, используя Imputer :
from sklearn.preprocessing import Imputer imp = Imputer(missing_values='NaN', strategy='mean', axis=0) imp.fit(features) test_df = replace_non_numeric(pd.read_csv("test.csv")) et.fit(features, labels) print et.predict(imp.transform(test_df[columns].values))
Если мы запустим его, он завершится успешно:
$ python et.py ['Fare', 'Pclass', 'Sex'] -> ET: 0.813692480359) [0 1 0 0 1 0 0 1 1 0 0 0 1 0 1 1 0 0 1 1 0 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 1 1 0 0 1 1 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 1 1 0 1 0 1 0 0 1 0 1 1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 0 0 1 1 1 1 1 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 1 1 1 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 1 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 0 0 1 0 1 0 0 1 0 1 0 0 1 0 0 1 1 1 1 0 0 1 0 0 0]
Последний шаг — добавить эти значения в наш тестовый фрейм данных, а затем записать это в файл, чтобы мы могли отправить его в Kaggle.
Тип этих значений — numpy.ndarray, который мы можем легко преобразовать в серию панд:
predictions = et.predict(imp.transform(test_df[columns].values)) test_df["Survived"] = pd.Series(predictions)
Затем мы можем записать столбцы PassengerId и Survived в файл:
test_df.to_csv("foo.csv", cols=['PassengerId', 'Survived'], index=False)
Then output file looks like this:
$ head -n5 foo.csv PassengerId,Survived 892,0 893,1 894,0
The code we’ve written is on github in case it’s useful to anyone.