【问题标题】:Hyperparameter Tuning k-means clustering超参数调整 k-means 聚类
【发布时间】:2020-09-10 08:19:44
【问题描述】:

我正在尝试通过在带有决策树分类器的管道中使用它来为时空 K-Means 聚类执行超参数调整。这个想法是使用 K-Means 聚类算法生成聚类距离空间矩阵和聚类标签,然后将其传递给决策树分类器。对于超参数调整,只需使用 K-Means 算法的参数。 我正在使用 Python 3.8 和 sklearn 0.22。

我感兴趣的数据有 3 列/属性:timexyxy 是空间坐标)。

代码是:

class ST_KMeans():
    """
    Note that the K-means clustering algorithm is designed for Euclidean distances.
    It may stop converging with other distances when the mean is no longer a
    best estimation for the cluster 'center'.

    The 'mean' minimizes squared differences (or, squared Euclidean distance).
    If you want a different distance function, you need to replace the mean with
    an appropriate center estimation.


    Parameters:

    k:  number of clusters
    
    eps1 : float, default=0.5
        The spatial density threshold (maximum spatial distance) between 
        two points to be considered related.

    eps2 : float, default=10
        The temporal threshold (maximum temporal distance) between two 
        points to be considered related.

    metric : string default='euclidean'
        The used distance metric - more options are
        ‘braycurtis’, ‘canberra’, ‘chebyshev’, ‘cityblock’, ‘correlation’,
        ‘cosine’, ‘dice’, ‘euclidean’, ‘hamming’, ‘jaccard’, ‘jensenshannon’,
        ‘kulsinski’, ‘mahalanobis’, ‘matching’, ‘rogerstanimoto’, ‘sqeuclidean’,
        ‘russellrao’, ‘seuclidean’, ‘sokalmichener’, ‘sokalsneath’, ‘yule’.
    
    n_jobs : int or None, default=-1
        The number of processes to start; -1 means use all processors (BE AWARE)


    Attributes:
    
    labels : array, shape = [n_samples]
        Cluster labels for the data - noise is defined as -1
    """

    def __init__(self, k, eps1 = 0.5, eps2 = 10, metric = 'euclidean', n_jobs = 1):
        self.k = k
        self.eps1 = eps1
        self.eps2 = eps2
        # self.min_samples = min_samples
        self.metric = metric
        self.n_jobs = n_jobs


    def fit(self, X):
        """
        Apply the ST K-Means algorithm 
        
        X : 2D numpy array. The first attribute of the array should be time attribute
            as float. The following positions in the array are treated as spatial
            coordinates.
            The structure should look like this [[time_step1, x, y], [time_step2, x, y]..]
            
            For example 2D dataset:
            array([[0,0.45,0.43],
            [0,0.54,0.34],...])


        Returns:

        self
        """
        
        # check if input is correct
        X = check_array(X)

        # type(X)
        # numpy.ndarray

        # Check arguments for DBSCAN algo-
        if not self.eps1 > 0.0 or not self.eps2 > 0.0:
            raise ValueError('eps1, eps2, minPts must be positive')

        # Get dimensions of 'X'-
        # n - number of rows
        # m - number of attributes/columns-
        n, m = X.shape


        # Compute sqaured form Euclidean Distance Matrix for 'time' and spatial attributes-
        time_dist = squareform(pdist(X[:, 0].reshape(n, 1), metric = self.metric))
        euc_dist = squareform(pdist(X[:, 1:], metric = self.metric))

        '''
        Filter the euclidean distance matrix using time distance matrix. The code snippet gets all the
        indices of the 'time_dist' matrix in which the time distance is smaller than 'eps2'.
        Afterward, for the same indices in the euclidean distance matrix the 'eps1' is doubled which results
        in the fact that the indices are not considered during clustering - as they are bigger than 'eps1'.
        '''
        # filter 'euc_dist' matrix using 'time_dist' matrix-
        dist = np.where(time_dist <= self.eps2, euc_dist, 2 * self.eps1)


        # Initialize K-Means clustering model-
        self.kmeans_clust_model = KMeans(
            n_clusters = self.k, init = 'k-means++',
            n_init = 10, max_iter = 300,
            precompute_distances = 'auto', algorithm = 'auto')

        # Train model-
        self.kmeans_clust_model.fit(dist)


        self.labels = self.kmeans_clust_model.labels_

        self.X_transformed = self.kmeans_clust_model.fit_transform(X)

        return self

    def transform(self, X):
        # print("\nX.shape = {0}\n".format(X.shape))
        # pass
        # return self.kmeans_clust_model.fit_transform(X)
        return self.X_transformed



# Initialize ST-K-Means object-
st_kmeans_algo = ST_KMeans(
    k = 5, eps1=0.6,
    eps2=9, metric='euclidean',
    n_jobs=1
    )

# Train on a chunk of dataset-
st_kmeans_algo.fit(data.loc[:500, ['time', 'x', 'y']])

# Get clustered data points labels-
kmeans_labels = st_kmeans_algo.labels

kmeans_labels.shape
# (501,)


# Get labels for points clustered using trained model-
kmeans_transformed = st_kmeans_algo.X_transformed

kmeans_transformed.shape
# (501, 5)

dtc = DecisionTreeClassifier()

dtc.fit(kmeans_transformed, kmeans_labels)

y_pred = dtc.predict(kmeans_transformed)

# Get model performance metrics-
accuracy = accuracy_score(kmeans_labels, y_pred)
precision = precision_score(kmeans_labels, y_pred, average='macro')
recall = recall_score(kmeans_labels, y_pred, average='macro')

print("\nDT model metrics are:")
print("accuracy = {0:.4f}, precision = {1:.4f} & recall = {2:.4f}\n".format(
    accuracy, precision, recall
    ))

# DT model metrics are:
# accuracy = 1.0000, precision = 1.0000 & recall = 1.0000


# Define steps of pipeline-
pipeline_steps = [
    ('st_kmeans_algo' ,ST_KMeans(k = 5, eps1=0.6, eps2=9, metric='euclidean', n_jobs=1)),
    ('dtc', DecisionTreeClassifier())
    ]

# Instantiate a pipeline-
pipeline = Pipeline(pipeline_steps)

# Train pipeline-
pipeline.fit(kmeans_transformed, kmeans_labels)

但是pipeline.fit() 给出以下错误:

> --------------------------------------------------------------------------- TypeError                                 Traceback (most recent call
> last) <ipython-input-25-711d6dd8d926> in <module>
> ----> 1 pipeline = Pipeline(pipeline_steps)
> 
> ~/.local/lib/python3.8/site-packages/sklearn/pipeline.py in
> __init__(self, steps, memory, verbose)
>     134         self.memory = memory
>     135         self.verbose = verbose
> --> 136         self._validate_steps()
>     137 
>     138     def get_params(self, deep=True):
> 
> ~/.local/lib/python3.8/site-packages/sklearn/pipeline.py in
> _validate_steps(self)
>     179             if (not (hasattr(t, "fit") or hasattr(t, "fit_transform")) or not
>     180                     hasattr(t, "transform")):
> --> 181                 raise TypeError("All intermediate steps should be "
>     182                                 "transformers and implement fit and transform "
>     183                                 "or be the string 'passthrough' "
> 
> TypeError: All intermediate steps should be transformers and implement
> fit and transform or be the string 'passthrough' '<__main__.ST_KMeans
> object at 0x7f0971db5430>' (type <class '__main__.ST_KMeans'>) doesn't

怎么了?

【问题讨论】:

    标签: python scikit-learn k-means hyperparameters


    【解决方案1】:

    您的错误消息说明了一切:所有中间步骤都应该是转换器并实施适合和转换。在您的情况下,您的类 ST_KMeans() 还必须实现 transform 函数才能在管道中使用。此外,最佳实践通常是从模块sklearn.base 中继承BaseEstimatorTransformerMixin 类:

    from sklearn.base import BaseEstimator, TransformerMixin
    
    
    class ST_KMeans(BaseEstimator, TransformerMixin):
    
        def fit(self, X, y=none):
            ...
            return self
    
        def transform(self, X):
            return self.X_transformed
    

    然后,您可以在管道中使用您的类。

    【讨论】:

    • 实施您建议的更改后,当我尝试使用 `pipeline.fit(kmeans_transformed, kmeans_labels)' 时,它给了我错误:“TypeError: fit() 需要 2 个位置参数,但 3 个是给定”?!
    • 您是否将y=None 添加到fit 函数的参数列表中?您的错误消息说该函数只需要 2 个位置参数。然而,在答案的定义中,它是 3(self、X 和 y)。
    • 是的,我做到了,现在它给出的新错误是:“ValueError: Expected 2D array, got scalar array instead: array=nan. Reshape your data either using array.reshape(-1, 1 ) 如果您的数据具有单个特征或 array.reshape(1, -1) 如果它包含单个样本。"
    • 好的,管道本身现在显然很好。该错误与通过管道传递的数据有关。既然你之前检查过kmeans_transformed 的形状,我想它是一个传递给管道的二维数组,对吧?这意味着错误消息与第一步无关,而是与管道中的第二步有关。请检查您是否返回功能以最终在ST_KMeanstransform 函数中训练您的分类器。因为看起来它实际上是在返回 Nan。
    • kmeans_transformed.shape 是 (501, 5),所以它是一个二维数组。如何检查“ST_KMeans”类的“transform()”方法返回了哪些特征?
    猜你喜欢
    • 2020-09-11
    • 2013-04-28
    • 2020-09-12
    • 2015-04-11
    • 2011-08-13
    • 2013-08-08
    • 2013-02-14
    • 2018-01-14
    • 2013-02-07
    相关资源
    最近更新 更多