未记录事实核验 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用边界与维护说明

本文适用于三种聚类算法的学习、参数比较及模拟RFM客户细分练习,不代表真实客户数据验证的业务结论。自动eps、MinPts=2×维度、指标推荐K值及算法选择助手均应作为候选建议,而非普适最优规则;find_optimal_k使用时须保证候选簇数小于样本数,且实际标签满足评分条件。混合类型数据需先明确编码和距离方案,不能直接将选择助手的推荐当作可执行方案。排除噪声后的评分仅描述保留样本,跨算法比较须结合有效样本覆盖范围、噪声比例和业务解释;若DBSCAN成为候选算法,输出中的-1仍表示噪声而非普通客户簇。RFM特征区分度图使用原始量纲的簇均值标准差,不能据柱高直接比较R、F、M的重要性。本次仅作结构与文本审阅,未运行代码或作外部事实核验。

Python聚类算法:无监督学习核心技术

聚类是无监督学习的重要分支,通过数据内在结构发现隐藏的模式和群体。本文深入探讨K-means、层次聚类和DBSCAN三种核心算法的数学原理、实现方法和实际应用。

聚类算法基础理论

无监督学习本质

聚类属于无监督学习范畴,目标是在没有标签信息的情况下,根据数据特征的相似性将样本分组:

$$\text{Input: } X = \{x_1, x_2, ..., x_n\} \in \mathbb{R}^{n \times d}$$
$$\text{Output: } C = \{C_1, C_2, ..., C_k\}, \quad C_i \cap C_j = \emptyset \text{ for } i \neq j$$

其中:

  • $X$ 是 $n$ 个 $d$ 维样本的数据矩阵
  • $C$ 是 $k$ 个不相交的簇集合
  • 对于覆盖全部样本的硬划分聚类,每个样本 $x_i$ 属于且仅属于一个簇;DBSCAN等含噪声识别的算法允许部分样本不归入任何簇。

相似性度量

聚类算法的核心是相似性度量。常用距离度量包括:

  1. 欧氏距离:$d(x_i, x_j) = \sqrt{\sum_{k=1}^{d} (x_{ik} - x_{jk})^2}$
  2. 曼哈顿距离:$d(x_i, x_j) = \sum_{k=1}^{d} |x_{ik} - x_{jk}|$
  3. 余弦距离:$d(x_i, x_j) = 1 - \frac{x_i \cdot x_j}{||x_i|| \cdot ||x_j||}$
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score, davies_bouldin_score, calinski_harabasz_score
from sklearn.datasets import make_blobs

K-means聚类算法

数学原理

K-means算法通过最小化簇内平方误差和(WCSS)来进行聚类:

$$J = \sum_{i=1}^{k} \sum_{x \in C_i} ||x - \mu_i||^2$$

其中:

  • $k$ 是预设的簇数量
  • $C_i$ 是第 $i$ 个簇
  • $\mu_i$ 是第 $i$ 个簇的中心点

算法步骤:

  1. 随机初始化 $k$ 个簇中心 $\mu_1, \mu_2, ..., \mu_k$
  2. 分配步骤:将每个点分配给最近的簇中心 $$c^{(i)} = \arg\min_j ||x^{(i)} - \mu_j||^2$$
  3. 更新步骤:重新计算每个簇的中心点 $$\mu_j = \frac{1}{|C_j|} \sum_{x \in C_j} x$$
  4. 重复步骤2和3直至收敛

K-means++初始化优化

标准K-means对初始中心点敏感,K-means++改进了初始化策略:

def demonstrate_kmeans():
    """K-means算法演示"""
    # 生成测试数据
    np.random.seed(42)
    X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=1.5, random_state=42)
    
    # 标准化数据
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # 比较不同初始化方法
    init_methods = ['random', 'k-means++']
    results = {}
    
    plt.figure(figsize=(12, 5))
    
    for i, init_method in enumerate(init_methods):
        # 训练模型
        kmeans = KMeans(n_clusters=4, init=init_method, random_state=42, n_init=1)
        labels = kmeans.fit_predict(X_scaled)
        centers = scaler.inverse_transform(kmeans.cluster_centers_)
        
        # 计算评估指标
        silhouette = silhouette_score(X_scaled, labels)
        davies_bouldin = davies_bouldin_score(X_scaled, labels)
        
        results[init_method] = {
            'silhouette_score': silhouette,
            'davies_bouldin_score': davies_bouldin,
            'inertia': kmeans.inertia_
        }
        
        # 可视化结果
        plt.subplot(1, 2, i+1)
        scatter = plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.6)
        plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='x', 
                   s=200, linewidths=3, label='聚类中心')
        plt.title(f'{init_method} 初始化\n轮廓系数: {silhouette:.3f}')
        plt.xlabel('特征1')
        plt.ylabel('特征2')
        plt.legend()
        plt.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    # 输出比较结果
    print("初始化方法比较:")
    for method, metrics in results.items():
        print(f"{method}:")
        print(f"  轮廓系数: {metrics['silhouette_score']:.3f}")
        print(f"  Davies-Bouldin指数: {metrics['davies_bouldin_score']:.3f}")
        print(f"  簇内平方和: {metrics['inertia']:.2f}")
    
    return results

# 运行演示
results = demonstrate_kmeans()

最优K值选择

使用肘部法则和轮廓分析确定最优簇数:

def find_optimal_k(X, max_k=10):
    """寻找最优K值"""
    k_range = range(2, max_k + 1)
    inertias = []
    silhouette_scores = []
    davies_bouldin_scores = []
    
    for k in k_range:
        kmeans = KMeans(n_clusters=k, init='k-means++', random_state=42)
        labels = kmeans.fit_predict(X)
        
        inertias.append(kmeans.inertia_)
        silhouette_scores.append(silhouette_score(X, labels))
        davies_bouldin_scores.append(davies_bouldin_score(X, labels))
    
    # 可视化评估结果
    fig, axes = plt.subplots(1, 3, figsize=(15, 4))
    
    # 肘部法则
    axes[0].plot(k_range, inertias, 'bo-')
    axes[0].set_xlabel('K值')
    axes[0].set_ylabel('簇内平方和 (WCSS)')
    axes[0].set_title('肘部法则')
    axes[0].grid(True, alpha=0.3)
    
    # 轮廓系数
    axes[1].plot(k_range, silhouette_scores, 'ro-')
    axes[1].set_xlabel('K值')
    axes[1].set_ylabel('轮廓系数')
    axes[1].set_title('轮廓系数分析')
    axes[1].grid(True, alpha=0.3)
    
    # Davies-Bouldin指数
    axes[2].plot(k_range, davies_bouldin_scores, 'go-')
    axes[2].set_xlabel('K值')
    axes[2].set_ylabel('Davies-Bouldin指数')
    axes[2].set_title('Davies-Bouldin指数 (越小越好)')
    axes[2].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    # 推荐最优K值
    optimal_k_silhouette = k_range[np.argmax(silhouette_scores)]
    optimal_k_db = k_range[np.argmin(davies_bouldin_scores)]
    
    print(f"基于轮廓系数的最优K值: {optimal_k_silhouette}")
    print(f"基于Davies-Bouldin指数的最优K值: {optimal_k_db}")
    
    return k_range, inertias, silhouette_scores, davies_bouldin_scores

层次聚类算法

算法原理

层次聚类构建数据点的层次结构,分为两种策略:

凝聚层次聚类(Agglomerative):自底向上,从单个数据点开始逐步合并 分裂层次聚类(Divisive):自顶向下,从整体数据开始逐步分割

连接准则

不同连接准则定义簇间距离的计算方法:

  1. Ward连接:最小化合并后簇的方差增量 $$d(C_i, C_j) = \sqrt{\frac{|C_i| \cdot |C_j|}{|C_i| + |C_j|}} ||\mu_i - \mu_j||$$

  2. Complete连接:两簇中最远点对的距离 $$d(C_i, C_j) = \max_{x \in C_i, y \in C_j} d(x, y)$$

  3. Average连接:两簇所有点对距离的平均值 $$d(C_i, C_j) = \frac{1}{|C_i| \cdot |C_j|} \sum_{x \in C_i} \sum_{y \in C_j} d(x, y)$$

  4. Single连接:两簇中最近点对的距离 $$d(C_i, C_j) = \min_{x \in C_i, y \in C_j} d(x, y)$$

from sklearn.datasets import make_circles
from scipy.cluster.hierarchy import dendrogram, linkage
from scipy.spatial.distance import pdist

def hierarchical_clustering_demo():
    """层次聚类演示"""
    # 生成测试数据
    np.random.seed(42)
    X, y_true = make_blobs(n_samples=150, centers=3, cluster_std=1.2, random_state=42)
    
    # 标准化数据
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # 比较不同连接方法
    linkage_methods = ['ward', 'complete', 'average', 'single']
    
    plt.figure(figsize=(16, 10))
    
    for i, method in enumerate(linkage_methods):
        # 层次聚类
        hierarchical = AgglomerativeClustering(n_clusters=3, linkage=method)
        labels = hierarchical.fit_predict(X_scaled)
        
        # 计算评估指标
        silhouette = silhouette_score(X_scaled, labels)
        
        # 聚类结果可视化
        plt.subplot(2, 4, i+1)
        scatter = plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.7)
        plt.title(f'{method.title()}连接\n轮廓系数: {silhouette:.3f}')
        plt.xlabel('特征1')
        plt.ylabel('特征2')
        plt.grid(True, alpha=0.3)
        
        # 树状图可视化
        plt.subplot(2, 4, i+5)
        if method == 'ward':
            # Ward方法需要使用欧氏距离
            Z = linkage(X_scaled, method=method)
        else:
            Z = linkage(X_scaled, method=method, metric='euclidean')
        
        dendrogram(Z, truncate_mode='level', p=3, no_labels=True)
        plt.title(f'{method.title()}连接\n树状图')
        plt.xlabel('样本索引')
        plt.ylabel('距离')
    
    plt.tight_layout()
    plt.show()
    
    return X_scaled, y_true

# 运行演示
X_scaled, y_true = hierarchical_clustering_demo()

DBSCAN密度聚类

算法原理

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)基于密度连接性进行聚类,能发现任意形状的簇并识别异常点。

核心概念:

  • 核心点:ε-邻域内至少包含MinPts个点的点
  • 边界点:不是核心点但在某个核心点的ε-邻域内
  • 噪声点:既不是核心点也不是边界点的点

数学定义: 给定点$p$和参数$\varepsilon$、$MinPts$:

  • $N_\varepsilon(p) = \{q \in X | d(p,q) \leq \varepsilon\}$ (ε-邻域)
  • 核心点:$|N_\varepsilon(p)| \geq MinPts$
  • 密度直达:若$q \in N_\varepsilon(p)$且$p$为核心点,则$q$从$p$密度直达
  • 密度可达:存在点链$p_1, p_2, ..., p_n$使得$p_{i+1}$从$p_i$密度直达
from sklearn.datasets import make_moons, make_circles
from sklearn.neighbors import NearestNeighbors

def dbscan_demonstration():
    """DBSCAN算法演示"""
    # 生成不同形状的数据集
    np.random.seed(42)
    
    # 月牙形数据
    X_moons, _ = make_moons(n_samples=300, noise=0.1, random_state=42)
    
    # 同心圆数据
    X_circles, _ = make_circles(n_samples=300, noise=0.05, factor=0.6, random_state=42)
    
    # 不规则形状数据
    X_blobs, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=42)
    
    datasets = [X_moons, X_circles, X_blobs]
    dataset_names = ['月牙形', '同心圆', '球形簇']
    
    plt.figure(figsize=(15, 10))
    
    for i, (X, name) in enumerate(zip(datasets, dataset_names)):
        # 标准化数据
        scaler = StandardScaler()
        X_scaled = scaler.fit_transform(X)
        
        # 原始数据
        plt.subplot(3, 4, i*4 + 1)
        plt.scatter(X[:, 0], X[:, 1], alpha=0.6)
        plt.title(f'{name}\n原始数据')
        plt.grid(True, alpha=0.3)
        
        # K-means聚类
        plt.subplot(3, 4, i*4 + 2)
        kmeans = KMeans(n_clusters=2, random_state=42)
        kmeans_labels = kmeans.fit_predict(X_scaled)
        plt.scatter(X[:, 0], X[:, 1], c=kmeans_labels, cmap='viridis', alpha=0.6)
        plt.title(f'{name}\nK-means')
        plt.grid(True, alpha=0.3)
        
        # 自动找最优eps参数
        neighbors = NearestNeighbors(n_neighbors=4)
        neighbors_fit = neighbors.fit(X_scaled)
        distances, indices = neighbors_fit.kneighbors(X_scaled)
        distances = np.sort(distances[:, -1])
        
        # 使用二阶差分启发式选取候选eps,不保证最优
        knee_point = np.argmax(np.diff(np.diff(distances))) + 2
        optimal_eps = distances[knee_point] if knee_point < len(distances) else 0.3
        
        # DBSCAN聚类
        plt.subplot(3, 4, i*4 + 3)
        dbscan = DBSCAN(eps=optimal_eps, min_samples=4)
        dbscan_labels = dbscan.fit_predict(X_scaled)
        plt.scatter(X[:, 0], X[:, 1], c=dbscan_labels, cmap='viridis', alpha=0.6)
        n_clusters = len(set(dbscan_labels)) - (1 if -1 in dbscan_labels else 0)
        n_noise = list(dbscan_labels).count(-1)
        plt.title(f'{name}\nDBSCAN\n簇数:{n_clusters}, 噪声:{n_noise}')
        plt.grid(True, alpha=0.3)
        
        # K-distance图(用于eps选择)
        plt.subplot(3, 4, i*4 + 4)
        plt.plot(distances)
        plt.axvline(x=knee_point, color='red', linestyle='--', alpha=0.7)
        plt.axhline(y=optimal_eps, color='red', linestyle='--', alpha=0.7)
        plt.title(f'{name}\nK-distance图\neps={optimal_eps:.3f}')
        plt.xlabel('点索引(按距离排序)')
        plt.ylabel('4-距离')
        plt.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    return datasets, dataset_names

# 运行演示
datasets, names = dbscan_demonstration()

聚类评估指标

核心评估方法

轮廓系数(Silhouette Score): $$s(i) = \frac{b(i) - a(i)}{\max\{a(i), b(i)\}}$$

其中:

  • $a(i)$:样本$i$到同簇内其他点的平均距离
  • $b(i)$:样本$i$到最近邻簇内所有点的平均距离
  • 取值范围:[-1, 1],越大越好

Davies-Bouldin指数: $$DB = \frac{1}{k} \sum_{i=1}^{k} \max_{j \neq i} \left\{ \frac{\sigma_i + \sigma_j}{d(c_i, c_j)} \right\}$$

其中$\sigma_i$是簇$i$内点到中心的平均距离,$d(c_i, c_j)$是簇中心间距离。取值越小越好。

def evaluate_clustering_metrics(X, labels):
    """聚类评估指标计算"""
    if len(set(labels)) < 2:
        return None
        
    # 处理噪声点
    if -1 in labels:
        mask = labels != -1
        X_clean = X[mask]
        labels_clean = labels[mask]
    else:
        X_clean = X
        labels_clean = labels
    
    if not (2 <= len(set(labels_clean)) < len(labels_clean)):
        return None
    
    # 计算评估指标
    silhouette = silhouette_score(X_clean, labels_clean)
    davies_bouldin = davies_bouldin_score(X_clean, labels_clean)
    calinski_harabasz = calinski_harabasz_score(X_clean, labels_clean)
    
    metrics = {
        'silhouette_score': silhouette,
        'davies_bouldin_score': davies_bouldin,
        'calinski_harabasz_score': calinski_harabasz,
        'n_clusters': len(set(labels_clean)),
        'n_noise': list(labels).count(-1) if -1 in labels else 0
    }
    
    return metrics

客户细分实战案例

RFM客户价值分析

基于客户消费行为的RFM模型进行聚类分析:

def customer_segmentation_case():
    """客户细分完整案例"""
    # 生成模拟客户RFM数据
    np.random.seed(42)
    n_customers = 1000
    
    # 创建不同客户群体的特征
    # 高价值客户:最近购买、频次高、金额大
    high_value = pd.DataFrame({
        'recency': np.random.exponential(10, 200),  # 最近购买天数
        'frequency': np.random.poisson(15, 200),   # 购买频次
        'monetary': np.random.lognormal(8, 0.5, 200)  # 消费金额
    })
    
    # 中等价值客户
    medium_value = pd.DataFrame({
        'recency': np.random.exponential(30, 400),
        'frequency': np.random.poisson(8, 400),
        'monetary': np.random.lognormal(6.5, 0.8, 400)
    })
    
    # 低价值客户
    low_value = pd.DataFrame({
        'recency': np.random.exponential(60, 300),
        'frequency': np.random.poisson(3, 300),
        'monetary': np.random.lognormal(5, 1, 300)
    })
    
    # 流失客户
    churned = pd.DataFrame({
        'recency': np.random.exponential(120, 100),
        'frequency': np.random.poisson(1, 100),
        'monetary': np.random.lognormal(4, 1.2, 100)
    })
    
    # 合并数据
    df = pd.concat([high_value, medium_value, low_value, churned], ignore_index=True)
    true_labels = np.array([0]*200 + [1]*400 + [2]*300 + [3]*100)
    
    # 添加噪声和异常值
    df['recency'] = np.clip(df['recency'], 1, 365)
    df['frequency'] = np.clip(df['frequency'], 1, 50)
    df['monetary'] = np.clip(df['monetary'], 10, 10000)
    
    print("客户RFM数据统计:")
    print(df.describe())
    
    # 特征标准化
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(df)
    
    # 比较不同聚类算法
    algorithms = {
        'K-means': KMeans(n_clusters=4, random_state=42),
        'Hierarchical': AgglomerativeClustering(n_clusters=4, linkage='ward'),
        'DBSCAN': DBSCAN(eps=0.8, min_samples=10)
    }
    
    results = {}
    plt.figure(figsize=(15, 10))
    
    for i, (name, algorithm) in enumerate(algorithms.items()):
        # 训练模型
        labels = algorithm.fit_predict(X_scaled)
        
        # 评估指标
        metrics = evaluate_clustering_metrics(X_scaled, labels)
        if metrics:
            results[name] = metrics
            results[name]['labels'] = labels
        
        # 可视化前两个主成分
        plt.subplot(2, 3, i+1)
        from sklearn.decomposition import PCA
        pca = PCA(n_components=2)
        X_pca = pca.fit_transform(X_scaled)
        
        scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap='viridis', alpha=0.6)
        plt.title(f'{name}\n轮廓系数: {metrics["silhouette_score"]:.3f}' if metrics else name)
        plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]:.1%})')
        plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]:.1%})')
        plt.grid(True, alpha=0.3)
        
        # RFM特征分布
        if i < 3:  # 只为前3个算法绘制
            plt.subplot(2, 3, i+4)
            feature_importance = []
            for j in range(3):  # R, F, M
                unique_labels = set(labels) - {-1} if -1 in labels else set(labels)
                if len(unique_labels) > 1:
                    cluster_means = []
                    for label in sorted(unique_labels):
                        mask = labels == label
                        cluster_means.append(df.iloc[mask, j].mean())
                    feature_importance.append(np.std(cluster_means))
                else:
                    feature_importance.append(0)
            
            plt.bar(['Recency', 'Frequency', 'Monetary'], feature_importance)
            plt.title(f'{name}\nRFM特征区分度')
            plt.ylabel('标准差')
            plt.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    # 选择最佳算法
    if results:
        best_algorithm = max(results.keys(), key=lambda x: results[x]['silhouette_score'])
        print(f"\n按当前有效样本轮廓系数选出的候选算法: {best_algorithm}(还需结合噪声比例和业务解释比较)")
        
        best_labels = results[best_algorithm]['labels']
        df['cluster'] = best_labels
        
        # 客户群体特征分析
        print("\n客户群体RFM特征分析:")
        cluster_analysis = df.groupby('cluster').agg({
            'recency': ['mean', 'std'],
            'frequency': ['mean', 'std'], 
            'monetary': ['mean', 'std'],
            'cluster': 'count'
        }).round(2)
        cluster_analysis.columns = ['_'.join(col).strip() for col in cluster_analysis.columns]
        print(cluster_analysis)
        
        # 客户群体标签化
        cluster_profiles = {}
        cluster_stats = df.groupby('cluster')[['recency', 'frequency', 'monetary']].mean()
        
        for cluster in cluster_stats.index:
            r_score = 'Low' if cluster_stats.loc[cluster, 'recency'] > df['recency'].median() else 'High'
            f_score = 'High' if cluster_stats.loc[cluster, 'frequency'] > df['frequency'].median() else 'Low'
            m_score = 'High' if cluster_stats.loc[cluster, 'monetary'] > df['monetary'].median() else 'Low'
            
            profile = f"R:{r_score}-F:{f_score}-M:{m_score}"
            cluster_profiles[cluster] = profile
        
        print("\n客户群体标签:")
        for cluster, profile in cluster_profiles.items():
            count = (df['cluster'] == cluster).sum()
            print(f"群体 {cluster}: {profile} ({count}人, {count/len(df)*100:.1f}%)")
    
    return df, results

# 运行客户细分案例
df_customers, clustering_results = customer_segmentation_case()

最佳实践与优化建议

算法选择决策树

def clustering_algorithm_selector(data_shape, data_type, noise_tolerance):
    """聚类算法选择助手"""
    n_samples, n_features = data_shape
    
    recommendations = []
    
    if data_type == 'spherical':  # 球形簇
        if n_samples < 10000:
            recommendations.append("K-means (快速收敛)")
            recommendations.append("层次聚类-Ward (可解释性强)")
        else:
            recommendations.append("Mini-batch K-means (内存友好)")
    
    elif data_type == 'arbitrary':  # 任意形状
        if noise_tolerance == 'high':
            recommendations.append("DBSCAN (处理噪声)")
        else:
            recommendations.append("谱聚类 (非凸簇)")
    
    elif data_type == 'mixed':  # 混合类型
        recommendations.append("K-means (基线)")
        recommendations.append("DBSCAN (发现异常)")
        recommendations.append("层次聚类 (层次结构)")
    
    print(f"数据特征: {n_samples}样本 × {n_features}特征")
    print(f"数据类型: {data_type}")
    print(f"噪声容忍度: {noise_tolerance}")
    print("推荐算法:")
    for i, rec in enumerate(recommendations, 1):
        print(f"{i}. {rec}")
    
    return recommendations

参数调优策略

  1. K-means: 使用肘部法则 + 轮廓分析
  2. DBSCAN: K-distance图确定eps,MinPts=2×维度
  3. 层次聚类: 根据业务需求选择连接准则

性能优化建议

  • 大数据集: 使用Mini-batch K-means或采样
  • 高维数据: 先进行PCA降维
  • 混合数据类型: 使用Gower距离
  • 在线聚类: 考虑流式算法

通过本文的学习,你已经掌握了Python中三种核心聚类算法的原理、实现和应用。在实际项目中,建议根据数据特征和业务需求选择合适的算法,并通过多种评估指标验证聚类效果。


相关链接:

标签:#Python #聚类算法 #无监督学习 #K-means #层次聚类 #DBSCAN #客户细分

创建时间:2024-06-29
最后更新:2025-01-02
字数统计:约6800字
阅读时长:45-50分钟