Курс Scalable
Machine Learning. Hadoop, Apache Spark, Python, ML -- вот это
всё.
Продолжаю
конспектировать пройденный курс. Неделя
4.
В прошлый
раз было про One-hot-encoding для трансформации
фич в числа и про хеширование как средство
снижения размерности датасета.
Пришло время
потрогать пройденные темы на практике.
Лабораторка.
CTR PREDICTION
PIPELINE LAB PREVIEW
In this segment, we'll
provide an overview
of the click-through
rate prediction pipeline
that you'll be working
on in this week's Spark coding lab.
The goal of the lab is
to implement a click-through rate
prediction pipeline
using various techniques that we've
The raw data consists
of a subset of a data
from a Kaggle
competition sponsored by Criteo.
This data includes 39
features describing users, ads,
and publishers.
Many of these features
contain a large number of categories
You'll next need to
extract features
to feed into a
supervised learning model.
And feature
extraction is the main focus of this lab.
You'll create OHE
features, as well as hashed features,
and store these
features using a sparse representation.
Экстрагировать
фичи это не так страшно как кажется.
Банальная трансформация. Типа текстовых
данных в числа; комбинированные фичи,
и т. д.
Given a set of
features, either OHE or hashed features,
you will use MLlib to
train logistic regression models.
You will then perform
Hyperparameter
tuning to search for a
good regularization parameter,
evaluating the results
via log loss,
and visualizing the
results of your grid search.
Понятно вроде.
ОК, забираем
нотебук
Запускаем
виртуалку
valik@snafu:~$ pushd ~/sparkvagrant/ valik@snafu:~/sparkvagrant$ vagrant up
И вперед: http://localhost:8001/tree
На Гитхабе
этот нотебук
Программа
действий:
-
Part 1: Featurize categorical data using one-hot-encoding (OHE)
-
Part 2: Construct an OHE dictionary
-
Part 3: Parse CTR data and generate OHE features: Visualization 1: Feature frequency
-
Part 4: CTR prediction and logloss evaluation: Visualization 2: ROC curve
-
Part 5: Reduce feature dimension via feature hashing: Visualization 3: Hyperparameter heat map
Для справки
Part 1: Featurize
categorical data using one-hot-encoding (OHE)
(1a) One-hot-encoding
Сначала создадим
словарь OHE вручную, для разминки возьмем
датасет из трех записей про трех животных.
# Data for manual OHE # Note: the first data point does not include any value for the optional third feature sampleOne = [(0, 'mouse'), (1, 'black')] sampleTwo = [(0, 'cat'), (1, 'tabby'), (2, 'mouse')] sampleThree = [(0, 'bear'), (1, 'black'), (2, 'salmon')] sampleDataRDD = sc.parallelize([sampleOne, sampleTwo, sampleThree]) sampleOHEDictManual = {} sampleOHEDictManual[(0,'bear')] = 0 sampleOHEDictManual[(0,'cat')] = 1 sampleOHEDictManual[(0,'mouse')] = 2 sampleOHEDictManual[(1,'black')] = 3 sampleOHEDictManual[(1,'tabby')] = 4 sampleOHEDictManual[(2,'mouse')] = 5 sampleOHEDictManual[(2,'salmon')] = 6 |
Всего семь
категорий.
(1b) Sparse vectors
https://spark.apache.org/docs/latest/api/python/pyspark.mllib.html#pyspark.mllib.linalg.SparseVector
Надо
потренироваться в создании sparse векторов.
Пока вручную.
import numpy as np from pyspark.mllib.linalg import SparseVector # TODO: Replace <FILL IN> with appropriate code aDense = np.array([0., 3., 0., 4.]) aSparse = SparseVector(len(aDense), enumerate(aDense)) bDense = np.array([0., 0., 0., 1.]) bSparse = SparseVector(len(bDense), enumerate(bDense)) w = np.array([0.4, 3.1, -1.4, -.5]) print aDense.dot(w) print aSparse.dot(w) print bDense.dot(w) print bSparse.dot(w) |
Что характерно,
несмотря на то, что такое решение
удовлетворяет условиям теста (умножение
дает одинаковые результаты), решение
неправильное.
Правильно
будет так:
aDense = np.array([0., 3., 0., 4.]) aSparse = SparseVector(len(aDense), {1: 3., 3: 4.}) bDense = np.array([0., 0., 0., 1.]) bSparse = SparseVector(len(bDense), [(3, 1.)])
Почему? Потому,
что гладиолус. Смотри определение
SparseVector.
(1c) OHE features as
sparse vectors
Теперь, когда
идея понятна, создадим SparseVector-ы для
игрушечного датасета с животными.
Если идея
непонятна, то вот: имеем семь категорий,
значит вектор будет длинной 7. Изначально
весь в нулях. Для каждой записи берем
такой вектор и ставим единички по номерам
из словаря, ключ в словаре – исходная
фича записи.
Пример:
(животное, мышка) в словаре дает 2. Значит
в SparseVector ставим единичку в позиции 2
(считая с 0).
# sampleOHEDictManual[(0,'bear')] = 0 # sampleOHEDictManual[(0,'cat')] = 1 # sampleOHEDictManual[(0,'mouse')] = 2 # sampleOHEDictManual[(1,'black')] = 3 # sampleOHEDictManual[(1,'tabby')] = 4 # sampleOHEDictManual[(2,'mouse')] = 5 # sampleOHEDictManual[(2,'salmon')] = 6 # sampleOne = [(0, 'mouse'), (1, 'black')] = 2, 3 sampleOneOHEFeatManual = SparseVector(7, {2: 1.0, 3: 1.0}) # sampleTwo = [(0, 'cat'), (1, 'tabby'), (2, 'mouse')] = 1, 4, 5 sampleTwoOHEFeatManual = SparseVector(7, {1: 1.0, 4: 1.0, 5: 1.0}) # sampleThree = [(0, 'bear'), (1, 'black'), (2, 'salmon')] = 0, 3, 6 sampleThreeOHEFeatManual = SparseVector(7, {0: 1.0, 3: 1.0, 6: 1.0}) |
Несложно,
правда? А я довольно долго колупался,
пока не сообразил, как надо правильно
записывать SparseVector-ы.
(1d) Define a OHE
function
Напишем функцию,
которая возвращает нам SparseVector для записи
исходного датасета.
def oneHotEncoding(rawFeats, OHEDict, numOHEFeats): """Produce a one-hot-encoding from a list of features and an OHE dictionary. Note: You should ensure that the indices used to create a SparseVector are sorted. Args: rawFeats (list of (int, str)): The features corresponding to a single observation. Each feature consists of a tuple of featureID and the feature's value. (e.g. sampleOne) sampleOne = [(0, 'mouse'), (1, 'black')] OHEDict (dict): A mapping of (featureID, value) to unique integer. OHE Dictionary example: sampleOHEDictManual[(0,'bear')] = 0 ... sampleOHEDictManual[(1,'black')] = 3 ... sampleOHEDictManual[(2,'salmon')] = 6 numOHEFeats (int): The total number of unique OHE features (combinations of featureID and value). Returns: SparseVector: A SparseVector of length numOHEFeats with indicies equal to the unique identifiers for the (featureID, value) combinations that occur in the observation and with values equal to 1.0. e.g. sampleOneOHEFeatManual = SparseVector(7, {2: 1.0, 3: 1.0}) """ spDict = {} для каждой фичи: key = значение из словаря ОХЕ if key is not None: spDict[key] = 1.0 res = SparseVector(numOHEFeats, spDict) return res # Calculate the number of features in sampleOHEDictManual numSampleOHEFeats = len(sampleOHEDictManual) # Run oneHotEnoding on sampleOne sampleOneOHEFeat = oneHotEncoding(sampleOne, sampleOHEDictManual, numSampleOHEFeats) |
(1e) Apply OHE to a
dataset
Ну, тут все
элементарно, применить функцию к
исходному датасету, получив закодированный
датасет, готовый к скармливанию в
logistic regression.
sampleOHEData = sampleDataRDD.map(lambda x: oneHotEncoding(x, sampleOHEDictManual, numSampleOHEFeats)) print sampleOHEData.collect()
[SparseVector(7, {2: 1.0, 3: 1.0}), SparseVector(7, {1: 1.0, 4: 1.0,
5: 1.0}), SparseVector(7, {0: 1.0, 3: 1.0, 6: 1.0})]
Part 2: Construct an
OHE dictionary
(2a) Pair RDD of
(featureID, category)
Надо
автоматизировать создание словаря. А
то он у нас был ручками записан.
Для начала
создадим RDD с уникальными значениями
фич из исходного списка списков.
create an RDD of
distinct (featureID, category) tuples
# sampleOne = [(0, 'mouse'), (1, 'black')] # sampleTwo = [(0, 'cat'), (1, 'tabby'), (2, 'mouse')] # sampleThree = [(0, 'bear'), (1, 'black'), (2, 'salmon')] # sampleDataRDD = sc.parallelize([sampleOne, sampleTwo, sampleThree]) sampleDistinctFeats = (sampleDataRDD .плоский список(lambda x: x) .выкинуть дубли()) |
(2b) OHE Dictionary
from distinct features
Вот теперь
можно сгенерировать словарь, сопоставив
уникальные категории номерам по порядку.
sampleOHEDict = (sampleDistinctFeats .сгенерить индексы() .собрать словарь()) print sampleOHEDict |
{(2, 'mouse'): 0, (0,
'cat'): 1, (0, 'bear'): 2, (2, 'salmon'): 3, (1, 'tabby'): 4, (1,
'black'): 5, (0, 'mouse'): 6}
(2c) Automated creation
of an OHE dictionary
Собираем лего:
напишем функцию, которая вернет нам
словарь для исходного датасета (исходный
датасет это список списков туплей).
def createOneHotDict(inputData): """Creates a one-hot-encoder dictionary based on the input data. Args: inputData (RDD of lists of (int, str)): An RDD of observations where each observation is made up of a list of (featureID, value) tuples. Returns: dict: A dictionary where the keys are (featureID, value) tuples and map to values that are unique integers. """ distinctFeats = (inputData .плоский список(lambda x: x) .выкинуть дубли()) res = (distinctFeats .сгенерить индексы() .собрать словарь()) return res sampleOHEDictAuto = createOneHotDict(sampleDataRDD) print sampleOHEDictAuto |
{(2, 'mouse'): 0, (0,
'cat'): 1, (0, 'bear'): 2, (2, 'salmon'): 3, (1, 'tabby'): 4, (1,
'black'): 5, (0, 'mouse'): 6}
На сегодня
хватит, продолжим завтра.
Следующий
номер программы:
Part 3: Parse CTR
data and generate OHE features
Visualization 1:
Feature frequency
original post http://vasnake.blogspot.com/2015/11/week-4-lab-4.html
Комментариев нет:
Отправить комментарий