Несовместимые результаты с KMeans между Apache Spark и scikit_learn - PullRequest
0 голосов
/ 18 мая 2018

Я выполняю кластеризацию на наборе данных, используя PySpark.Чтобы найти число кластеров, я выполнил кластеризацию по диапазону значений (2,20) и нашел значения wsse (сумма квадратов внутри кластера) для каждого значения k.Это где я нашел что-то необычное.Насколько я понимаю, при увеличении количества кластеров wsse монотонно уменьшается.Но результаты, которые я получил, говорят иначе.Я отображаю wsse только для первых нескольких кластеров

Results from spark

For k = 002 WSSE is 255318.793358
For k = 003 WSSE is 209788.479560
For k = 004 WSSE is 208498.351074
For k = 005 WSSE is 142573.272672
For k = 006 WSSE is 154419.027612
For k = 007 WSSE is 115092.404604
For k = 008 WSSE is 104753.205635
For k = 009 WSSE is 98000.985547
For k = 010 WSSE is 95134.137071

Если вы посмотрите на значение wsse для k=5 и k=6, вы увидите, что wsse увеличилось,Я повернулся к sklearn, чтобы увидеть, получаю ли я похожие результаты.Коды, которые я использовал для spark и sklearn, находятся в приложении к концу поста.Я попытался использовать те же значения для параметров в модели KMeans для spark и sklearn.Ниже приведены результаты от sklearn, и они, как я и ожидал, будут монотонно убывающими.

Results from sklearn

For k = 002 WSSE is 245090.224247
For k = 003 WSSE is 201329.888159
For k = 004 WSSE is 166889.044195
For k = 005 WSSE is 142576.895154
For k = 006 WSSE is 123882.070776
For k = 007 WSSE is 112496.692455
For k = 008 WSSE is 102806.001664
For k = 009 WSSE is 95279.837212
For k = 010 WSSE is 89303.574467

Я не уверен, почему я увеличиваю значения wsse в Spark.Я пытался использовать разные наборы данных и обнаружил там похожее поведение.Есть ли место, где я иду не так?Любые подсказки были бы отличными.


ПРИЛОЖЕНИЕ

Набор данных находится здесь .

Считать данные и установить переменные объявления

# get data
import pandas as pd
url = "https://raw.githubusercontent.com/vectosaurus/bb_lite/master/3.0%20data/adult_comp_cont.csv"

df_pandas = pd.read_csv(url)
df_spark = sqlContext(df_pandas)
target_col = 'high_income'
numeric_cols = [i for i in df_pandas.columns if i !=target_col]

k_min = 2 # 2 in inclusive
k_max = 21 # 2i is exlusive. will fit till 20

max_iter = 1000
seed = 42    

Этот код я использую для получения результатов sklearn :

from sklearn.cluster import KMeans as KMeans_SKL
from sklearn.preprocessing import StandardScaler as StandardScaler_SKL

ss = StandardScaler_SKL(with_std=True, with_mean=True)
ss.fit(df_pandas.loc[:, numeric_cols])
df_pandas_scaled = pd.DataFrame(ss.transform(df_pandas.loc[:, numeric_cols]))

wsse_collect = []

for i in range(k_min, k_max):
    km = KMeans_SKL(random_state=seed, max_iter=max_iter, n_clusters=i)
    _ = km.fit(df_pandas_scaled)
    wsse = km.inertia_
    print('For k = {i:03d} WSSE is {wsse:10f}'.format(i=i, wsse=wsse))
    wsse_collect.append(wsse)

Это код, который я использую для получения результатов искры

from pyspark.ml.feature import StandardScaler, VectorAssembler
from pyspark.ml.clustering import KMeans

standard_scaler_inpt_features = 'ss_features'
kmeans_input_features = 'features'
kmeans_prediction_features = 'prediction'


assembler = VectorAssembler(inputCols=numeric_cols, outputCol=standard_scaler_inpt_features)
assembled_df = assembler.transform(df_spark)

scaler = StandardScaler(inputCol=standard_scaler_inpt_features, outputCol=kmeans_input_features, withStd=True, withMean=True)
scaler_model = scaler.fit(assembled_df)
scaled_data = scaler_model.transform(assembled_df)

wsse_collect_spark = []

for i in range(k_min, k_max):
    km = KMeans(featuresCol=kmeans_input_features, predictionCol=kmeans_prediction_col,
                        k=i, maxIter=max_iter, seed=seed)
    km_fit = km.fit(scaled_data)
    wsse_spark = km_fit.computeCost(scaled_data)
    wsse_collect_spark .append(wsse_spark)
    print('For k = {i:03d} WSSE is {wsse:10f}'.format(i=i, wsse=wsse_spark))

ОБНОВЛЕНИЕ

После @Michail N'sответ, я изменил значения tol и maxIter для модели Spark KMeans.Я перезапустил код, но увидел, что повторяется то же самое поведение.Но так как Михаил упомянул

Spark, MLlib, фактически, реализует K-средства ||

, я увеличил число initSteps в 50 раз и повторнозапустил процесс, который дал следующие результаты:

For k = 002 WSSE is 255318.718684
For k = 003 WSSE is 212364.906298
For k = 004 WSSE is 185999.709027
For k = 005 WSSE is 168616.028321                                               
For k = 006 WSSE is 123879.449228                                               
For k = 007 WSSE is 113646.930680                                               
For k = 008 WSSE is 102803.889178                                               
For k = 009 WSSE is 97819.497501                                                
For k = 010 WSSE is 99973.198132                                                
For k = 011 WSSE is 89103.510831                                                
For k = 012 WSSE is 84462.110744                                                
For k = 013 WSSE is 78803.619605                                                
For k = 014 WSSE is 82174.640611                                                
For k = 015 WSSE is 79157.287447                                                
For k = 016 WSSE is 75007.269644                                                
For k = 017 WSSE is 71610.292172                                                
For k = 018 WSSE is 68706.739299                                                
For k = 019 WSSE is 65440.906151                                                
For k = 020 WSSE is 66396.106118

Увеличение wsse с k=5 и k=6 исчезает.Хотя поведение сохраняется, если вы посмотрите на k=13 и k=14 и в других местах, но, по крайней мере, я узнал, откуда это происходит.

1 Ответ

0 голосов
/ 18 мая 2018

Нет ничего плохого в том, что WSSE не уменьшается монотонно.Теоретически WSSE должен монотонно уменьшаться, если кластер является оптимальным, это означает, что из всех возможных кластеров k-центров тот, который имеет лучший WSSE.

Проблема в том, что K-means не обязательно может найти оптимальную кластеризацию для данного k.Его итеративный процесс может сходиться от случайной начальной точки к локальному минимуму, который может быть хорошим, но не оптимальным.

Существуют такие методы, как K-means ++ и Kmeans ||у которых есть варианты алгоритмов выбора, которые с большей вероятностью выбирают разнородные, разделенные центроиды и более надежно приводят к хорошей кластеризации, а Spark MLlib, фактически, реализует K-means ||.Однако все они все еще имеют элемент случайности в выборе и не могут гарантировать оптимальную кластеризацию.

Случайный начальный набор кластеров, выбранный для k = 6, возможно, привел к особенно неоптимальной кластеризации, или он мог остановитьсяраньше, чем он достиг своего локального оптимума.

Вы можете улучшить его, изменив параметры Kmeans вручную.Алгоритм имеет пороговое значение через tol, которое контролирует минимальное количество движения центроида кластера, которое считается значительным, где более низкие значения означают, что алгоритм K-средних позволит центроидам продолжать двигаться дольше.

Увеличение максимального числа итераций с помощью maxIter также предотвращает его преждевременную остановку за счет возможного большего объема вычислений.

Поэтому я советую перезапустить кластеризацию с помощью

 ...
 #increase from default 20
 max_iter= 40     
 #decrase from default 0.0001
 tol = 0.00001 
 km = KMeans(featuresCol=kmeans_input_features, predictionCol=kmeans_prediction_col, k=i, maxIter=max_iter, seed=seed , tol = tol )
 ...
...