Aufgrund des Einflusses von Corona hat sich die Zeit, die zu Hause verbracht wird, erhöht. Ab April dieses Jahres begann ich, hart am maschinellen Lernen zu arbeiten. Dabei hatte ich viele Möglichkeiten, an Kaggle- und SIGNATE-Wettbewerben zu arbeiten, und beschloss, einen Artikel über Qiita als eine Ausgabe zu schreiben. Dieses Mal werden wir an der SIGNATE-Übung "Prognose des Preises für private Beherbergungsdienste" arbeiten. Ziel ist es, einen Benchmark für tiefere Analysen und Einsichten zu erstellen. Der diesmal erstellte Code verbleibt im Jupyter Notebook-Format in hier.
In dieser Aufgabe werden wir daran arbeiten, ein Modell zu erstellen, das den Unterkunftspreis für jede Immobilie anhand der auf Airbnb, einem privaten Beherbergungsdienst, veröffentlichten Immobiliendaten vorhersagt. Bei Airbnb legen die Eigentümer die Zimmerpreise basierend auf Zimmergröße und Lage fest, aber es scheint nicht einfach zu sein, angemessene Preise festzulegen.
Importieren Sie die erforderlichen Bibliotheken und laden Sie die Trainingsdaten, Verifizierungsdaten und Übermittlungsdaten.
#Bibliothek importieren
import numpy as np
import pandas as pd
from pandas import DataFrame, Series
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from scipy.stats import norm,skew
from sklearn.preprocessing import LabelEncoder
import lightgbm as lgb
import warnings
warnings.filterwarnings('ignore')
#Daten lesen
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
sub = pd.read_csv('sample_submit.csv',names=('id','pred'))
#Geben Sie die Anzahl der Zeilen als Variable an
ntrain = train.shape[0]
ntest = test.shape[0]
#Überprüfen Sie die Anzahl der Daten
train.shape, test.shape
#((55583, 29), (18528, 28))
Es kann bestätigt werden, dass die Trainingsdaten 55583 Elemente und die Verifizierungsdaten 18528 Elemente enthalten. Da der Unterkunftspreis ** y **, den Sie endgültig vorhersagen möchten, nur in den Trainingsdaten enthalten ist, ist die Anzahl der Spalten um eins höher als die Verifizierungsdaten. Zeigen Sie die ersten 5 Zeilen der Trainings- und Validierungsdaten an.
train.head()
test.head()
** Annehmlichkeiten ** und ** Name ** sind wie Zeichenfolgen. Da der Klassifizierer keine Zeichendaten verarbeiten kann, müssen Gegenmaßnahmen berücksichtigt werden. Wenn alle 29 Spalten mit Trainingsdaten überprüft wurden, waren die Spalten mit Zeichenfolgen, die durch die Kategoriekonvertierung nicht behandelt werden konnten, wie folgt.
Headername | Erläuterung |
---|---|
amenities | Ausstattung |
description | Erläuterung |
name | Name des Anwesens |
thumbnail_url | Daumenbild-Link |
Visualisieren Sie die Verteilung der Zielvariablen ** y (Raumpreis) **.
sns.distplot(train['y']);
Wie Sie in der Wettbewerbsübersicht sehen können, ist der Problemtyp die Regression. Für die Regression ist es wichtig, dass die Werte der Zielvariablen einer Normalverteilung folgen. Es stellt sich heraus, dass ** y ** weit von einer Normalverteilung entfernt ist, also müssen wir uns damit befassen. Überprüfen Sie auch die aktuelle Verzerrung und Schärfe.
#Zeigen Sie Verzerrung und Schärfe
print("Schiefe: %f" % train['y'].skew())
print("Kurtosis: %f" % train['y'].kurt())
#Schiefe: 4.264338
#Kurtosis: 26.030945
Die ** Verzerrung ** betrug 4,26 und die ** Schärfe ** betrug 26,03. Es ist ziemlich voreingenommen. Für die wichtigsten kategorialen Variablen werden wir auch die Beziehung zu ** y ** anhand des Box-Whisker-Diagramms untersuchen. (accommodates)
var = 'accommodates'
data = pd.concat([train['y'], train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="y", data=data)
fig.axis(ymin=0, ymax=2100);
plt.xticks(rotation=90);
→ Die Immobilie mit dem höchsten Durchschnittspreis bietet Platz für 16 Personen. Die Host-Seite (die Seite, die die Immobilie vermietet) scheint dazu zu neigen, den Unterkunftspreis höher zu setzen, wenn die Immobilie eine große Kapazität hat.
(bathrooms)
var = 'bathrooms'
data = pd.concat([train['y'], train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="y", data=data)
fig.axis(ymin=0, ymax=2100);
plt.xticks(rotation=90);
(bedrooms)
var = 'bedrooms'
data = pd.concat([train['y'], train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="y", data=data)
fig.axis(ymin=0, ymax=2100);
plt.xticks(rotation=90);
(beds)
var = 'beds'
data = pd.concat([train['y'], train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="y", data=data)
fig.axis(ymin=0, ymax=2100);
plt.xticks(rotation=90);
Es scheint, dass der Preis umso höher ist, je mehr Variablen es gibt. Als Beispiel, das auf den Visualisierungsergebnissen basiert, wird hier angenommen, dass das Hinzufügen von ** {Bad | Bett} -Räumen ** zu einer verbesserten Genauigkeit führen kann.
In der Phase "Daten abrufen" wissen wir, dass die Zielvariablen keiner Normalverteilung folgen. Nehmen Sie als Gegenmaßnahme den Logarithmus von ** y ** und setzen Sie ihn in eine Pseudo-Normalverteilung. Lassen Sie uns auch eine Diagnose mit einem normalen QQ-Diagramm stellen, indem wir einen Rest nehmen, um zu sehen, ob er sich einer Normalverteilung nähert.
#Vor der Verarbeitung
sns.distplot(train['y'] , fit=norm);
#Parameter abrufen
(mu, sigma) = norm.fit(train['y'])
print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma))
#Visualisierung
plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)],loc='best')
plt.ylabel('Frequency')
plt.title('y distribution')
#Anwenden regelmäßiger QQ-Diagramme
fig = plt.figure()
res = stats.probplot(train['y'], plot=plt)
plt.show()
#Nach der Behandlung
# log1p(Numpy-Funktion)Bewerben, Protokoll nehmen
train["y"] = np.log1p(train["y"])
#Überprüfen Sie die Verteilung nach der Anwendung
sns.distplot(train['y'] , fit=norm);
#Parameter abrufen
(mu, sigma) = norm.fit(train['y'])
print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma))
#Visualisierung
plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)],loc='best')
plt.ylabel('Frequency')
plt.title('y distribution')
#Anwenden regelmäßiger QQ-Diagramme
fig = plt.figure()
res = stats.probplot(train['y'], plot=plt)
plt.show()
Wenn Sie den Logarithmus nehmen, können Sie sehen, dass er nahe an der Normalverteilung liegt. Sie können sehen, dass die Residuen von ** y ** auf der roten 45-Grad-Linie ausgerichtet sind, obwohl es einige Abweichungen im regulären QQ-Diagramm gibt. Daher kann gesagt werden, dass der Fehler der Zielvariablen auch der Normalverteilung folgt.
#Spalte y extrahieren
train_y = train['y']
train_y.shape
#(55583,)
#Kombinieren Sie Trainingsdaten und Validierungsdaten
all_data = pd.concat((train, test)).reset_index(drop=True)
all_data.drop(['y','id'], axis=1, inplace=True)
print("all_data size : {}".format(all_data.shape))
#all_data size : (74111, 27)
Bei der Datenanalyse folgt immer die Verarbeitung fehlender Werte. Lassen Sie uns die fehlenden Werte in jeder Spalte noch einmal behandeln.
#Überprüfen Sie den Prozentsatz der fehlenden Werte
all_data_na = (all_data.isnull().sum() / len(all_data)) * 100
all_data_na = all_data_na.drop(all_data_na[all_data_na == 0].index).sort_values(ascending=False)[:30]
missing_data = pd.DataFrame({'Missing Ratio' :all_data_na})
missing_data.head(15)
Es scheint, dass 13 Variablen fehlende Werte haben. Lassen Sie uns entscheiden, wie die Daten aufgefüllt oder gelöscht werden sollen, während wir die Originaldaten und Visualisierungsergebnisse betrachten. Dieses Mal werde ich ** thumbnail_url ** und ** Postleitzahl ** löschen.
#Konvertieren Sie alle jahr / Monat-bezogenen Variablen in Gleitkomma-Typen und füllen Sie fehlende Werte mit 0
for c in ('first_review','last_review','host_since'):
all_data[c] = pd.to_datetime(all_data[c])
all_data[c] = pd.DatetimeIndex(all_data[c])
all_data[c] = np.log(all_data[c].values.astype(np.float64))
all_data[c] = all_data[c].fillna(0)
#Füllen Sie fehlende Werte mit 1 aus
for c in ('bathrooms','beds','bedrooms'):
all_data[c] = all_data[c].fillna(1)
#Füllen Sie fehlende Werte mit Keine aus
for c in ('host_response_rate','neighbourhood','host_identity_verified','host_has_profile_pic'):
all_data[c] = all_data[c].fillna('None')
#Mit Median füllen
all_data['review_scores_rating'].fillna(all_data['review_scores_rating'].median(),inplace=True)
#Nicht verwendete Spalten löschen
all_data = all_data.drop(['thumbnail_url','zipcode'],axis=1)
Jetzt haben Sie sich mit den fehlenden Werten befasst. Überprüfen Sie hier den Datentyp.
all_data.dtypes
Es gibt ziemlich viele Objekttypen. Da das Modell nicht so trainiert werden kann, wie es ist, wird ** Label Encoder ** verwendet.
#Etikettencodierung
cols = ('bed_type','cancellation_policy','city','cleaning_fee','host_identity_verified','host_has_profile_pic','host_response_rate','instant_bookable','property_type','room_type','neighbourhood')
for c in cols:
lbl = LabelEncoder()
lbl.fit(list(all_data[c].values))
all_data[c] = lbl.transform(list(all_data[c].values))
#Überprüfen Sie den Datentyp
all_data.dtypes
Ich konnte es in einen fast numerischen Typ umwandeln.
Viele Wettbewerbe, die sich mit Textdaten befassen, finden bei kaggle und SIGNATE statt. Da es mit natürlichen Sprachen wie Japanisch und Englisch umgeht, wird es als Natural Language Processing (NLP) bezeichnet und hat sich als Bereich des maschinellen Lernens etabliert. Im Vergleich zu Tabellendaten gibt es keinen großen Unterschied in der Lern- / Vorhersagephase, da diese aufgrund der Art des Wettbewerbs nicht vom Rahmen des überwachten maschinellen Lernens abweicht. Andererseits gibt es in Bezug auf die Vorverarbeitung verschiedene Typen wie die Wortstammextraktion und die Wortvektorisierung. Dieses Mal werden wir uns mit einer einfachen Methode befassen, die nur die Anzahl der Zeichen im Satz zählt.
#Zählen Sie die Anzahl der Zeichen in der Zielspalte
for c in ('amenities','description','name'):
all_data[c] = all_data[c].apply(lambda x: sum(len(word) for word in str(x).split(" ")))
all_data.dtypes
Sie können sehen, dass es sich bei allen um numerische Typen handelt. Das Histogramm von ** description ** wird übrigens als Beispiel angezeigt.
#Beschreibung Histogramm
plt.hist(all_data['description'],alpha=0.5)
plt.xlabel('description')
plt.ylabel('count')
plt.show()
Sie können sehen, dass die überwiegende Mehrheit der Eigenschaften eine Beschreibung von 800 Zeichen oder mehr hat.
Fügen wir zum Schluss die ** {Bad | Bett} -Zimmer ** hinzu, die wir zum Zeitpunkt der EDA angenommen haben.
#Erstellen Sie eine neue Feature-Menge
#Addieren Sie die Anzahl der Badezimmer und Schlafzimmer
all_data['total_rooms'] = all_data['bathrooms'] + all_data['bedrooms']
Dieses Mal werden wir LightGBM, das in letzter Zeit am häufigsten in Wettbewerben verwendet wird, in GBDT (Gradient Boosting Tree) verwenden, um vom Lernen bis zur Vorhersage zu arbeiten.
#Teilen Sie in Trainingsdaten und Verifizierungsdaten auf
train = all_data[:ntrain]
test = all_data[ntrain:]
#Lernen mit LGBM Regressor
model = lgb.LGBMRegressor(num_leaves=100,learning_rate=0.05,n_estimators=1000)
model.fit(train,train_y)
#Vorhersage von Validierungsdaten mit dem trainierten Modell
pred = np.expm1(model.predict(test))
Überprüfen Sie unter feature_importances_, welche Variablen zum Modell beitragen.
#Visualisieren Sie die Bedeutung von Variablen
ranking = np.argsort(-model.feature_importances_)
f, ax = plt.subplots(figsize=(11, 9))
sns.barplot(x=model.feature_importances_[ranking],y=train.columns.values[ranking], orient='h')
ax.set_xlabel('feature importance')
plt.tight_layout()
plt.show()
Die beiden wichtigsten Wichtigkeitsstufen waren Längen- und Breitengrad. Schreiben Sie es schließlich in eine CSV-Datei und senden Sie es ab.
#Einreichung
sub['pred'] = pred
sub.to_csv('sub.csv',index=False,header=None)
Aufgrund der Vorverarbeitung und des Lernens wurde es mit 44/163 bewertet (Stand: 6. Oktober 2020). Es ist ein anständiges Ergebnis, aber es gibt in jeder Hinsicht Raum für Verbesserungen. (Verwendung von verworfenen Funktionen, Verarbeitung natürlicher Sprache, Modellauswahl, Überprüfung von Schnittpunkten, Parameteranpassung, Ensemble ...) Mit diesem Einfallsreichtum können noch bessere Ergebnisse erzielt werden. Ich hoffe, dieser Artikel wird Ihnen helfen.
・ Kaggle "Hauspreise: Fortgeschrittene Regressionstechniken" veröffentlichtes Notizbuch "Gestapelte Regressionen: Top 4% auf LeaderBoard"