Es kann schwierig sein zu wissen, was es wirklich wert ist. Weniger Details können einen großen Preisunterschied bewirken. Zum Beispiel kostet einer dieser Pullover 335 US-Dollar und der andere 9,99 US-Dollar. Kannst du erraten was was ist?
Angesichts der Anzahl der online verkauften Produkte ist die Preisgestaltung von Produkten noch schwieriger. Der Preis für Kleidung weist starke saisonale Preisentwicklungen auf und wird stark vom Markennamen beeinflusst. Der Preis für elektronische Geräte schwankt jedoch aufgrund der Produktspezifikationen.
Japans größte Community-gesteuerte Shopping-App ist sich dieses Problems sehr bewusst. Es ist schwierig, dem Verkäufer ein gutes Preisangebot zu unterbreiten, da der Verkäufer alles oder jeden auf den Mercari-Markt bringen kann.
Die Mercari Price Suggestion Challenge ist ein Wettbewerb, der den "angemessenen Preis" eines Produkts anhand der tatsächlichen Produktdaten schätzt, die zum Verkauf angeboten wurden. Die Produktdaten umfassen den Produktnamen, die Produktbeschreibung, den Produktstatus, den Markennamen, den Kategorienamen usw., und basierend auf diesen wird der Verkaufspreis durch maschinelles Lernen vorhergesagt.
Der Produktdatensatz wird von der nordamerikanischen Version von Mercari veröffentlicht, sodass jeder ihn erhalten kann. https://www.kaggle.com/c/mercari-price-suggestion-challenge/data
Dieses Mal möchte ich diese Daten verwenden, um den angemessenen Preis zu schätzen.
train.tsv hat die Daten von 1,5 Millionen tatsächlich aufgelisteten Artikeln. Alle Notationen sind aufgrund der nordamerikanischen Version der Mercari-Daten in englischer Sprache. Das Produkt wird aus 8 Spalten beschrieben.
Säule | Erläuterung |
---|---|
train_id | User Post ID |
name | Produktname |
item_condition_id | Produktzustand |
category_name | Produktkategorie |
brand_name | Markenname |
price | Verkaufspreis (Dollar) |
shipping | Versandkosten (Aussteller oder Käufer) |
item_description | Produktbeschreibung |
Diese Daten sind in Zug und Test unterteilt, und der Verkaufspreis wird durch maschinelles Lernen vorhergesagt.
Die Ausführungsumgebung befindet sich in Google Colaboratory. Da die Anzahl der Daten extrem groß ist, dauert es einige Zeit, es sei denn, es befindet sich in einer GPU-Umgebung.
Das Goggle Colboratory finden Sie hier. Google Colaboratory - Übersicht und Anweisungen (TensorFlow und GPU können verwendet werden)
RSMLE wird verwendet, wenn Sie die Verteilung nahe der ** logarithmischen Normalverteilung ** und den ** Fehler zwischen dem gemessenen Wert und dem vorhergesagten Wert als Verhältnis oder Verhältnis ** anstelle einer Breite ausdrücken möchten.
In der obigen Abbildung sieht das Produktpreishistogramm wie eine logarithmische Normalverteilung aus. Zum Beispiel auch Die Fehlerbreiten von (1000, 5000) und (100000, 104000) betragen 4000, aber die Fehlerverhältnisse sind unterschiedlich, und dieser Unterschied ist groß.
Ab diesem Zeitpunkt scheint der geschätzte Preis für die Bewertungsmethode von RMSLE geeignet zu sein.
Es wird nicht nur train.tsv, sondern auch test.tsv veröffentlicht. Da es jedoch kein korrektes Antwortetikett gibt, werden die Daten, die durch Entfernen von etwa 10.000 Elementen aus train.tsv erhalten wurden, als Testdaten verwendet.
Gesamtdaten (* 1482535, 8 ) -> (train_df ( 1472535, 8 ), test_df ( 10000, 7 *))
Es gibt viele Leerzeichen in Kategorien, Marken und Produktbeschreibungen. Beim maschinellen Lernen ist es normal, Fehler zu verarbeiten. Füllen Sie daher die Lücken mit der folgenden Funktion aus. Infolge des Fehlens machte die Marke "vermisst" 42% der Gesamtzahl aus.
def handle_missing_inplace(dataset):
dataset['category_name'].fillna(value="Other", inplace=True)
dataset['brand_name'].fillna(value='missing', inplace=True)
dataset['item_description'].fillna(value='None', inplace=True)
Die Marke wird vor der Typumwandlung geschnitten. Da es ungefähr 5000 Arten von Marken gibt, sind Markennamen, die extrem selten vorkommen, für das Lernen nicht sehr nützlich. Geben Sie daher dasselbe "Fehlen" wie das Leerzeichen ein.
pop_brands = df["brand_name"].value_counts().index[:NUM_BRANDS]
df.loc[~df["brand_name"].isin(pop_brands), "brand_name"] = "missing"
Als ich es halbierte, war die Mindestanzahl von Auftritten viermal.
Konvertiert Textdaten in Kategorietyp. Dies liegt daran, dass bei der späteren Verarbeitung Dummy-Variablen erstellt werden.
def to_categorical(dataset):
dataset['category_name'] = dataset['category_name'].astype('category')
dataset['brand_name'] = dataset['brand_name'].astype('category')
dataset['item_condition_id'] = dataset['item_condition_id'].astype('category')
Wendet CountVectorizer auf Produktnamen und Kategorienamen an. Einfach ausgedrückt wird CountVectorizer entsprechend der Anzahl der Vorkommen vektorisiert. Wenn Sie beispielsweise Count Vectorizer für die drei Produktnamen "MLB Cincinnati Reds T-Shirt Größe XL", "AVA-VIV Bluse" und "Lederpferdestatuen" ausführen, werden diese wie folgt vektorisiert.
Da der Produktname vom Verkäufer eingegeben wird, kann es außerdem zu Tippfehlern in Wörtern oder festen Wörtern oder Zahlen kommen, die nur in bestimmten Sätzen vorkommen. Fügen Sie vor diesem Hintergrund die Option min_df zu CountVectorizer hinzu. min_df bedeutet, Wörter auszuschließen, die kleiner als min_df% sind.
count_name = CountVectorizer(min_df=NAME_MIN_DF)
X_name = count_name.fit_transform(df["name"])
count_category = CountVectorizer()
X_category = count_category.fit_transform(df["category_name"])
Im Gegensatz zu CountVectorizer berücksichtigt TfidfVectorizer nicht nur die Anzahl der Vorkommen eines Wortes, sondern auch die Seltenheit des Wortes. Beispielsweise erscheinen Wörter, die in jedem Satz vorhanden sind, wie "desu" und "masu", und Akronyme wie "a" und "the" häufig im Englischen und werden von solchen Wörtern in Count Vectorizer stark in Mitleidenschaft gezogen. Stattdessen wird es verwendet, wenn Sie eine Vektorisierung durchführen möchten, die sich auf die Wichtigkeit von Wörtern konzentriert.
Mit anderen Worten bedeutet TfidfVecotrizer "Gewichtung von Wörtern, die häufig in einem Dokument und selten in einem anderen Dokument mit hoher Bedeutung vorkommen".
Ab den obigen Punkten wird die Produktbeschreibung von TfidfVectorizer vektorisiert.
Dann ist die Tabelle wie oben gezeigt, und der tfidf-Wert ist stark an das Akronym und den Konnektiv gebunden. Geben Sie stop_word = 'english'
an, da solche Wörter beim Lernen immer noch keine Bedeutung haben.
Als nächstes zeigt die Abbildung links die unteren 10 tfidf-Werte. Wenn der tfidf-Wert extrem klein ist, macht er wenig Sinn. Löschen Sie ihn daher. Anstatt tfidf für ein Wort zu verwenden, nehmen Sie tfidf für aufeinanderfolgende Wörter. Lassen Sie uns zum Beispiel n-Gramm mit dem Sprichwort "Ein Apfel am Tag hält den Arzt fern" setzen.
n-gram(1, 2)
{'an': 0, 'apple': 2, 'day': 5, 'keeps': 9, 'the': 11,'doctor':7,'away': 4,
'an apple': 1, 'apple day': 3, 'day keeps': 6, 'keeps the': 10,
'the doctor': 12, 'doctor away': 8}
n-gram(1, 3)
{'an': 0, 'apple': 3, 'day': 7, 'keeps': 12, 'the': 15, 'doctor': 10,'away': 6,
'an apple': 1, 'apple day': 4, 'day keeps': 8, 'keeps the': 13,'the doctor': 16,
'doctor away': 11, 'an apple day': 2, 'apple day keeps': 5, 'day keeps the': 9,
'keeps the doctor': 14, 'the doctor away': 17}
Auf diese Weise werden mit zunehmendem n-Gramm-Bereich die Eigenschaften des Textes detaillierter erfasst und nützliche Daten erfasst. Mit den zusätzlichen Optionen sieht es wie in der Abbildung rechts aus.
Das endgültige tfidf sieht wie in der folgenden Abbildung aus. Der mit dem höchsten tfidf-Wert ist "description", was durch "Noch keine Beschreibung" beeinflusst werden kann. Sie können sehen, dass neue und gebrauchte Artikel wie "neu" und "gebraucht" auch den Preis beeinflussen.
tfidf_descp = TfidfVectorizer(max_features = MAX_FEAT_DESCP,
ngram_range = (1,3),
stop_words = "english")
X_descp = tfidf_descp.fit_transform(df["item_description"])
Wie ich bereits erwähnt habe, gibt es ungefähr 5.000 Arten von Marken, und als Ergebnis des Schneidprozesses gibt es ungefähr 2.500 Arten von Marken.
Beschriften Sie diese mit 0 oder 1. Da es viele Daten gibt, setzen Sie sparse_output = True
und führen Sie aus.
label_brand = LabelBinarizer(sparse_output=True)
X_brand = label_brand.fit_transform(df["brand_name"])
Dummy-Variablen sind eine Technik zum Konvertieren nicht numerischer Daten in Zahlen. Insbesondere werden nicht numerische Daten in eine Folge von Zahlen mit nur "0" und "1" konvertiert. Hier werden Dummy-Variablen für den Status des Produkts und die Versandkosten erstellt.
X_dummies = scipy.sparse.csr_matrix(pd.get_dummies(df[[
"item_condition_id", "shipping"]], sparse = True).values, dtype=int)
Nachdem wir alle Spalten verarbeitet haben, werden wir alle Sequenzen kombinieren und auf das Modell anwenden.
X = scipy.sparse.hstack((X_dummies,
X_descp,
X_brand,
X_category,
X_name)).tocsr()
Da nicht alle Parameter erklärt werden können, werden einige Parameter kurz zusammengefasst.
option | desc |
---|---|
alpha | Normalisierungsgrad zur Verhinderung von Überlernen |
max_iter | Maximale Anzahl von Lerniterationen |
tol | Vorbehaltlich einer Erhöhung der Punktzahl um Tol oder höher |
alpha
Es ist möglich, ein Modell zu erstellen, das übermäßig zu den angegebenen Daten passt und einen kleinen Fehler für die angegebenen Trainingsdaten verursacht. Es wird jedoch als "** Übertraining " bezeichnet, dass es nicht möglich ist, eine geeignete Vorhersage für unbekannte Daten zu treffen. sagen. Daher kann ein Überlernen verhindert werden, indem Einschränkungen für das Parameterlernen festgelegt werden. Eine solche Einschränkung wird als " Normalisierung **" bezeichnet.
option | description |
---|---|
n_esimators | Anzahl der ermittelten Bäume |
learning_rate | Gewicht jedes Baumes |
max_depth | Maximale Tiefe jedes Baumes |
num_leaves | Anzahl der Blätter |
min_child_samples | Mindestanzahl der im Endknoten enthaltenen Daten |
n_jobs | Anzahl paralleler Prozesse |
learning_rate
n_estimatiors
Ridge
Suchen Sie zunächst nach dem optimalen Alpha-Wert. Bewegen Sie Alpha im Bereich von 0,05 bis 75, um den Effekt auf die Genauigkeit zu visualisieren
Aus der Figur wurde der Mindestwert * RMSLE 0,4745938085035464 * erhalten, wenn Alpha = 3,0.
Als nächstes wurde als Ergebnis der Überprüfung der maximalen Anzahl von Suchen max_iter in allen Bereichen keine Verbesserung der Genauigkeit erhalten. Je höher der Tol-Wert ist, desto ungenauer war er.
Aus dem Obigen wird der Ridge-Parameter mit Alpha = 3 modelliert.
LGBM Zur Anpassung der LGBM-Parameter habe ich mich auf die Dokumente bezogen. https://lightgbm.readthedocs.io/en/latest/Parameters-Tuning.html
Es scheint eine Standardpraxis zu sein, zunächst Learning_rate und n_estimatiors als ersten Schritt zum Anpassen der Parameter von LGBM festzulegen. Um die Genauigkeit zu verbessern, scheint die Lernrate klein und die Schätzer groß zu sein. Bewegen Sie die Lernrate in den Bereich von 0,05 bis 0,7, um n_Anschätzer anzupassen.
Verschieben Sie als Nächstes num_leaves, nachdem Sie learning_rate und n_estimatiors festgelegt haben.
(num_leaves = 20) RMSLE 0.4620242411418184 ↓ (num = 31) RMSLE 0.4569169142862856 ↓ (num = 40) RMSLE 0.45587232757584967
Insgesamt haben wir festgestellt, dass das Erhöhen von num_leaves auch die Genauigkeit verbessert. Insgesamt bedeutet hier auch, wenn andere Parameter eingestellt werden.
Wenn jedoch bei der Anpassung jedes Parameters num_leaves zu stark angehoben wurde, kam es zu einer ** Überanpassung **, und in einigen Fällen konnte keine gute Punktzahl erzielt werden. Ich musste es gut mit anderen Parametern einstellen.
Wenn learning_rate = 0,7 max_depth = 15 ist, ist num_leaves = 30 RMSLE 44.650714399639845
Das endgültige LGBM-Modell sieht folgendermaßen aus:
lgbm_params = {'n_estimators': 1000, 'learning_rate': 0.4, 'max_depth': 15,
'num_leaves': 40, 'subsample': 0.9, 'colsample_bytree': 0.8,
'min_child_samples': 50, 'n_jobs': 4}
Mit Rideg + LGBM wird der vorhergesagte Wert berechnet. LGBM hat eine bessere Punktzahl als Ridge, aber durch die Kombination der beiden Modelle können Sie die Genauigkeit verbessern. Ridge RMSL error on dev set: 0.47459370995217937
LGBM RMSL error on dev set: 0.45317097672035855
Ridge + LGBM RMSL error on dev set: 0.4433081424824549
Diese Genauigkeit ist ein geschätzter Fehlerbereich von 18,89 bis 47,29 für ein 30-Dollar-Produkt.
Preis ist der von Ridge + LGBM vorhergesagte Wert, und real_price ist der gemessene Wert. Von den 10.000 Testdaten gab es ungefähr 7553 mit Fehlern innerhalb von 10 USD.
Restgrundstück mit Protokoll
Verteilung der tatsächlichen und geschätzten Preise
Ich habe einfach die Differenz genommen, aber es gibt ungefähr 90 Produkte, die eine Differenz von 100 Dollar oder mehr zwischen dem vorhergesagten Wert und dem gemessenen Wert haben. Da es sich bei diesem Datensatz um Daten von vor zwei Jahren handelt, ist ersichtlich, dass die Anzahl der Daten gering ist und keine guten Vorhersagen möglich sind, da Apple Watch usw. relativ neue Produkte sind. Es ist auch Mercari. Es ist gut, aber aufgrund der auf persönlichen Werten basierenden Preisgestaltung kann nicht alles gut vorhergesagt werden. Eigentlich wurde die Coach-Tasche für etwa 9 US-Dollar verkauft ...
import numpy as np
import pandas as pd
import scipy
from sklearn.linear_model import Ridge
from lightgbm import LGBMRegressor
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.preprocessing import LabelBinarizer
NUM_BRANDS = 2500
NAME_MIN_DF = 10
MAX_FEAT_DESCP = 10000
print("Reading in Data")
df = pd.read_csv('train.tsv', sep='\t')
print('Formatting Data')
shape = df.shape[0]
train_df = df[:shape-10000]
test_df = df[shape-10000:]
target = test_df.loc[:, 'price'].values
target = np.log1p(target)
print("Concatenate data")
df = pd.concat([train_df, test_df], 0)
nrow_train = train_df.shape[0]
y_train = np.log1p(train_df["price"])
def handle_missing_inplace(dataset):
dataset['category_name'].fillna(value="Othe", inplace=True)
dataset['brand_name'].fillna(value='missing', inplace=True)
dataset['item_description'].fillna(value='None', inplace=True)
print('Handle missing')
handle_missing_inplace(df)
def to_categorical(dataset):
dataset['category_name'] = dataset['category_name'].astype('category')
dataset['brand_name'] = dataset['brand_name'].astype('category')
dataset['item_condition_id'] = dataset['item_condition_id'].astype('category')
print('Convert categorical')
to_categorical(df)
print('Cut')
pop_brands = df["brand_name"].value_counts().index[:NUM_BRANDS]
df.loc[~df["brand_name"].isin(pop_brands), "brand_name"] = "missing"
print("Name Encoders")
count_name = CountVectorizer(min_df=NAME_MIN_DF)
X_name = count_name.fit_transform(df["name"])
print("Category Encoders")
count_category = CountVectorizer()
X_category = count_category.fit_transform(df["category_name"])
print("Descp encoders")
tfidf_descp = TfidfVectorizer(max_features = MAX_FEAT_DESCP,
ngram_range = (1,3),
stop_words = "english")
X_descp = tfidf_descp.fit_transform(df["item_description"])
print("Brand encoders")
label_brand = LabelBinarizer(sparse_output=True)
X_brand = label_brand.fit_transform(df["brand_name"])
print("Dummy Encoders")
X_dummies = scipy.sparse.csr_matrix(pd.get_dummies(df[[
"item_condition_id", "shipping"]], sparse = True).values, dtype=int)
X = scipy.sparse.hstack((X_dummies,
X_descp,
X_brand,
X_category,
X_name)).tocsr()
print("Finished to create sparse merge")
X_train = X[:nrow_train]
X_test = X[nrow_train:]
model = Ridge(solver='auto', fit_intercept=True, alpha=3)
print("Fitting Rige")
model.fit(X_train, y_train)
print("Predicting price Ridge")
preds1 = model.predict(X_test)
def rmsle(Y, Y_pred):
assert Y.shape == Y_pred.shape
return np.sqrt(np.mean(np.square(Y_pred - Y )))
print("Ridge RMSL error on dev set:", rmsle(target, preds1))
def rmsle_lgb(labels, preds):
return 'rmsle', rmsle(preds, labels), False
train_X, valid_X, train_y, valid_y = train_test_split(X_train, y_train, test_size=0.3, random_state=42)
lgbm_params = {'n_estimators': 1000, 'learning_rate': 0.4, 'max_depth': 15,
'num_leaves': 40, 'subsample': 0.9, 'colsample_bytree': 0.8,
'min_child_samples': 50, 'n_jobs': 4}
model = LGBMRegressor(**lgbm_params)
print('Fitting LGBM')
model.fit(train_X, train_y,
eval_set=[(valid_X, valid_y)],
eval_metric=rmsle_lgb,
early_stopping_rounds=100,
verbose=True)
print("Predict price LGBM")
preds2 = model.predict(X_test)
print("LGBM RMSL error on dev set:", rmsle(target, preds2))
preds = (preds1 + preds2) / 2
print("Ridge + LGBM RMSL error on dev set:", rmsle(target, preds))
test_df["price1"] = np.expm1(preds1)
test_df['price2']=np.exp(preds2)
test_df['price']= np.expm1(preds)
test_df['real_price'] = np.expm1(target)
Durch die Schätzung des fairen Preises haben wir eine bessere Punktzahl als erwartet erzielt. Ich denke, dass sich die Genauigkeit etwas mehr verbessert hätte, wenn der min_df-Wert, die Einstellung des n-Gramm-Bereichs usw. im Vorverarbeitungsteil geändert worden wären und der Text nicht nur auf tfidf angewendet, sondern detailliertere Korrekturen vorgenommen hätte. .. Außerdem sind die Werte der Produkte für jede Person unterschiedlich, sodass Sie nur einen gewissen Grad vorhersagen können. Wenn Sie es hilfreich finden, geben Sie mir bitte einen guten Knopf!
Recommended Posts