[PYTHON] (Fortsetzung) Probieren Sie andere Entfernungsfunktionen mit kmeans in Scikit-learn aus

Was in diesem Artikel einzuführen

Im vorherigen Artikel (http://qiita.com/Kensuke-Mitsuzawa/items/67c17f6bda8a1097740c) habe ich eingeführt, dass kmeans andere Entfernungsfunktionen als die euklidische Entfernung versucht.

Sie können auch Euklidisch verwenden. Es gibt jedoch einen Fall, in dem eine andere Distanzfunktion eine bessere Clusterbildung ist. Warum also nicht mal probieren? Es ist der Inhalt.

Übrigens kam ich letztes Mal zu dem Schluss: "Ich habe versucht, die Distanzfunktion des Pearson-Korrelationskoeffizienten zu verwenden, aber hey!".

Das ist nicht praktisch. Also habe ich verschiedene Dinge ausprobiert.

Try1 verwendet die cdist-Methode

scipy hat eine Methode namens cdist.

Dies ist eine sehr nützliche Methode, selbst wenn sie eine Ähnlichkeitsskala auf einer Entfernungsskala implementiert.

Wenn Sie keine implementierte Skala haben, können Sie diese selbst definieren.

Definieren wir also unsere eigene Distanzfunktion für den Pearson-Korrelationskoeffizienten.

#Distanzfunktion mit cdist
def special_operation(X, Y):
    coeficient = scipy.stats.pearsonr(X, Y)[0]
    #coeficient = numpy.corrcoef(X, Y)[0][1]

    if coeficient < 0:
        return abs(coeficient)
    else:
        return 1 - coeficient

def special_pearsonr_corrcoef(X, Y):
    return cdist(X, Y, special_operation)

special_operation () ist eine Funktion, die X- und Y-Vektoren verwendet und einen Float zurückgibt.

Wenn Sie diese Funktion mit "cdist ()" umschließen, verfügen Sie über eine Methode, mit der der Abstand zwischen Daten in kürzester Zeit umfassend berechnet werden kann.

Überschreiben Sie die euklidische Funktion wie zuvor mit dem Affenfeld.

# pearson distance with numpy cdist
def new_euclidean_distances(X, Y=None, Y_norm_squared=None, squared=False):
    return special_pearsonr_corrcoef(X, Y)
from sklearn.cluster import k_means_
k_means_.euclidean_distances = new_euclidean_distances
kmeans_model = KMeans(n_clusters=3, random_state=10, init='random').fit(features)
print(kmeans_model.labels_)
elapsed_time = time.time() - start
print ("Special Coeficient k-means:{0}".format(elapsed_time))
print('='*40)

Die Ergebnisse werden später vorgestellt

Schreiben Sie mit Try2 Cython

Neuimplementierung mit Cython für noch schnellere Geschwindigkeiten.

Ich dachte: "Ist es nicht immer noch langsam, wenn ich scipy auch mit cython anrufe?" Also kopiere ich den relevanten Teil von scipy und füge ihn in C ein.

Ich mache auch Typdefinitionen, um so viel wie möglich zu tun.

Kompilieren Sie es und wenn es fertig ist, patchen Sie es erneut mit Affen.

# -*- coding: utf-8 -*-
#cython: boundscheck=False
#cython: wraparound=False
__author__ = 'kensuke-mi'

import numpy as np
cimport numpy as np
cimport cython
from itertools import combinations
from scipy.stats import pearsonr
import time
import scipy.special as special
from scipy.special import betainc

ctypedef np.float64_t FLOAT_t


def _chk_asarray(np.ndarray[FLOAT_t, ndim=1] a, axis):
    if axis is None:
        a = np.ravel(a)
        outaxis = 0
    else:
        a = np.asarray(a)
        outaxis = axis

    if a.ndim == 0:
        a = np.atleast_1d(a)

    return a, outaxis


def ss(np.ndarray[FLOAT_t, ndim=1] a, int axis=0):
    """
    Squares each element of the input array, and returns the sum(s) of that.
    Parameters
    ----------
    a : array_like
        Input array.
    axis : int or None, optional
        Axis along which to calculate. Default is 0. If None, compute over
        the whole array `a`.
    Returns
    -------
    ss : ndarray
        The sum along the given axis for (a**2).
    See also
    --------
    square_of_sums : The square(s) of the sum(s) (the opposite of `ss`).
    Examples
    --------
    >>> from scipy import stats
    >>> a = np.array([1., 2., 5.])
    >>> stats.ss(a)
    30.0
    And calculating along an axis:
    >>> b = np.array([[1., 2., 5.], [2., 5., 6.]])
    >>> stats.ss(b, axis=1)
    array([ 30., 65.])
    """
    cdef np.ndarray[FLOAT_t, ndim=1] new_a
    cdef int new_axis

    new_a, new_axis = _chk_asarray(a, axis)
    return np.sum(a*a, axis)

def betai(FLOAT_t a, FLOAT_t b, FLOAT_t x):
    """
    Returns the incomplete beta function.
    I_x(a,b) = 1/B(a,b)*(Integral(0,x) of t^(a-1)(1-t)^(b-1) dt)
    where a,b>0 and B(a,b) = G(a)*G(b)/(G(a+b)) where G(a) is the gamma
    function of a.
    The standard broadcasting rules apply to a, b, and x.
    Parameters
    ----------
    a : array_like or float > 0
    b : array_like or float > 0
    x : array_like or float
        x will be clipped to be no greater than 1.0 .
    Returns
    -------
    betai : ndarray
        Incomplete beta function.
    """
    cdef FLOAT_t typed_x = np.asarray(x)
    cdef FLOAT_t betai_x = np.where(typed_x < 1.0, typed_x, 1.0)  # if x > 1 then return 1.0
    return betainc(a, b, betai_x)


def pearsonr(np.ndarray[FLOAT_t, ndim=1] x, np.ndarray[FLOAT_t, ndim=1] y):
    """
    Calculates a Pearson correlation coefficient and the p-value for testing
    non-correlation.
    The Pearson correlation coefficient measures the linear relationship
    between two datasets. Strictly speaking, Pearson's correlation requires
    that each dataset be normally distributed. Like other correlation
    coefficients, this one varies between -1 and +1 with 0 implying no
    correlation. Correlations of -1 or +1 imply an exact linear
    relationship. Positive correlations imply that as x increases, so does
    y. Negative correlations imply that as x increases, y decreases.
    The p-value roughly indicates the probability of an uncorrelated system
    producing datasets that have a Pearson correlation at least as extreme
    as the one computed from these datasets. The p-values are not entirely
    reliable but are probably reasonable for datasets larger than 500 or so.
    Parameters
    ----------
    x : (N,) array_like
        Input
    y : (N,) array_like
        Input
    Returns
    -------
    (Pearson's correlation coefficient,
     2-tailed p-value)
    References
    ----------
    http://www.statsoft.com/textbook/glosp.html#Pearson%20Correlation
    """
    # x and y should have same length.
    cdef np.ndarray[FLOAT_t, ndim=1] typed_x = np.asarray(x)
    cdef np.ndarray[FLOAT_t, ndim=1] typed_y = np.asarray(y)
    cdef int n = len(typed_x)
    cdef float mx = typed_x.mean()
    cdef float my = typed_y.mean()
    cdef np.ndarray[FLOAT_t, ndim=1] xm, ym
    cdef FLOAT_t r_den, r_num, r, prob, t_squared
    cdef int df


    xm, ym = typed_x - mx, typed_y - my
    r_num = np.add.reduce(xm * ym)
    r_den = np.sqrt(ss(xm) * ss(ym))
    r = r_num / r_den

    # Presumably, if abs(r) > 1, then it is only some small artifact of floating
    # point arithmetic.
    r = max(min(r, 1.0), -1.0)
    df = n - 2
    if abs(r) == 1.0:
        prob = 0.0
    else:
        t_squared = r**2 * (df / ((1.0 - r) * (1.0 + r)))
        prob = betai(0.5*df, 0.5, df/(df+t_squared))

    return r, prob

cdef special_pearsonr(np.ndarray[FLOAT_t, ndim=1] X, np.ndarray[FLOAT_t, ndim=1] Y):
    cdef float pearson_coefficient
    pearsonr_value = pearsonr(X, Y)
    pearson_coefficient = pearsonr_value[0]
    
    if pearson_coefficient < 0:
        return abs(pearson_coefficient)
    else:
        return 1 - pearson_coefficient


def copy_inverse_index(row_col_data_tuple):
    return (row_col_data_tuple[1], row_col_data_tuple[0], row_col_data_tuple[2])


def sub_pearsonr(np.ndarray X, int row_index_1, int row_index_2):
    cdef float pearsonr_distance = special_pearsonr(X[row_index_1], X[row_index_2])

    return (row_index_1, row_index_2, pearsonr_distance)


cdef XY_both_2dim(np.ndarray[FLOAT_t, ndim=2] X, np.ndarray[FLOAT_t, ndim=2] Y):
    cdef int row = X.shape[0]
    cdef int col = Y.shape[0]
    cdef np.ndarray[FLOAT_t, ndim=2] pearsonr_divergence_set = np.zeros([row, col])
    cdef float pearson_value = 0.0 

    for x_index in xrange(row):
        x_array = X[x_index]
        for y_index in xrange(col):
            y_array = Y[y_index]
            pearson_value = special_pearsonr(x_array, y_array)
            pearsonr_divergence_set[x_index, y_index] = pearson_value 
    
    return pearsonr_divergence_set


def pearsonr_distances(X, Y, Y_norm_squared=None, squared=False): 
    #start = time.time()
    
    cdef np.ndarray[FLOAT_t, ndim=2] pearsonr_divergence_set = XY_both_2dim(X, Y)
    #elapsed_time = time.time() - start
    #print ("Pearsonr(Cython version) k-means:{0}".format(elapsed_time))

    return pearsonr_divergence_set

Try3 Verwenden Sie andere Distanzfunktionen

Wie ich letztes Mal vorgestellt habe, ist das Ergebnis eine Tabelle, wenn K-Mittel-Clustering unter Verwendung verschiedener Distanzfunktionen durchgeführt wird.

41094b24-3bcb-0d04-93ab-edc54eb59501.png

Betrachtet man dies, so liefern sowohl die cos-Ähnlichkeit als auch die Jackard-Ähnlichkeit (Distanzfunktion aus) gute Ergebnisse.

Es ist nicht erforderlich, sich an den Pearson-Korrelationskoeffizienten zu halten. Wenn Sie gute Ergebnisse erzielen, können Sie es versuchen.

Diese beiden werden vorbereitet, nachdem sie als Distanzfunktion in "cdist" von "scipy" implementiert wurden, sodass Sie sie sofort verwenden können, indem Sie einfach die Parameter schreiben.

cos Entfernung

def new_euclidean_distances(X, Y=None, Y_norm_squared=None, squared=False):
    return cdist(X, Y, 'cosine')

Jackard Abstand

def new_euclidean_distances(X, Y=None, Y_norm_squared=None, squared=False):
    return cdist(X, Y, 'jaccard')

Ich habe versucht zu messen

Ich habe nur die Bearbeitungszeit gemessen. Ich habe die Clustergenauigkeit nicht überprüft, da ich keine beschrifteten Daten verwende. (Weil der Zweck dieser Zeit nur darin besteht, mich zu motivieren, die im Papier gezeigte Methode anzuwenden)

Die Daten sind die gleichen wie im vorherigen Artikel. Schauen wir sie uns der Reihe nach an.

Erstens ist der normale euklidische Abstand. Die Zahlen sind die Clusterbezeichnung und die Ausführungszeit.

[1 1 0 1 1 2 1 1 2 1 1 1 2 1 1 0 2 1 0 0 2 0 2 0 1 1 1 0 2 2 2 2 2 2 0 2 2]
Normal k-means:0.00853204727173
========================================

cos Entfernung

[2 2 0 2 2 1 2 2 1 0 2 2 1 0 2 0 1 2 0 0 1 0 1 0 2 2 2 0 1 0 1 1 1 1 0 1 1]
Cosine k-means:0.0154190063477
========================================

Jackard Abstand

[0 0 0 0 1 0 0 0 2 0 0 0 1 0 0 0 0 0 2 2 0 2 0 2 0 0 0 0 2 2 0 0 0 0 0 0 0]
jaccard k-means:0.0656130313873

Pearson-Korrelationskoeffizient unter Verwendung von cdist

[0 0 0 1 1 2 1 0 2 1 0 2 2 0 0 1 1 0 1 0 2 0 2 0 2 2 1 1 2 1 1 2 2 2 1 2 2]
Special Coeficient k-means:9.39525008202
========================================

cythonisierter Pearson-Korrelationskoeffizient

[0 0 0 1 1 2 1 0 2 1 0 2 2 0 0 1 1 0 1 0 2 0 2 0 2 2 1 1 2 1 1 2 2 2 1 2 2]
Pearsonr k-means:9.40655493736
========================================

Offensichtlich ist die Ausführungszeit des Pearson-Korrelationskoeffizienten nutzlos. Die Geschwindigkeit ist

Euklidischer <Jackard <cos <(unüberwindbare Mauer) <Pearson

war.

Das Ergebnis des Jackard-Koeffizientenabstands unterscheidet sich erheblich von den anderen Cluster-Labels ...

Möglicherweise weisen die Eingabedaten diesmal eine geringe Anzahl von Funktionen auf (eine Situation, die sich erheblich vom Dokumentclustering unterscheidet), sodass sich das Ergebnis möglicherweise erheblich von den anderen unterscheidet.

An dieser Stelle möchte ich eine weitere Gelegenheit nutzen, um dies gründlich zu überprüfen.

Zusammenfassung

Um andere Distanzfunktionen mit den k-Mitteln von scikit-learn zu verwenden,

Recommended Posts

(Fortsetzung) Probieren Sie andere Entfernungsfunktionen mit kmeans in Scikit-learn aus
kmeans ++ mit scikit-learn
Definieren Sie Ihre eigene Distanzfunktion mit k-Mitteln des Scikit-Lernens
Versuchen Sie es mit Scikit-Learn (1) - K-Clustering nach Durchschnittsmethode
Identifizieren Sie Ausreißer mit dem Random Forest Classifier von scikit-learn
SVM versucht maschinelles Lernen mit Scikit-Learn
Versuchen Sie, sich mit Python bei qiita anzumelden
Probieren Sie SVM mit scikit-learn auf Jupyter Notebook aus
Versuchen Sie, Python mit Google Cloud-Funktionen zu verwenden
Clustering repräsentativer Schulen im Sommer 2016 mit Scikit-Learn
Füllen Sie fehlende Werte mit Scikit-learn impute aus
[Fortsetzung] Versuchen Sie den Zugriff auf das SPS-Register mit Python
Versuchen Sie, mit Mongo in Python auf dem Mac zu arbeiten
Erhalten Sie mit QGIS Längen- und Breitengrade in Metern
Versuchen Sie, Videos mit OpenCV in Echtzeit zu konvertieren