mcpField
M
ml-supply-chain

ml-supply-chain

github.com/kishorkukreja数据与数据库8451 次安装3 周前更新
代码仓库

关于 ml-supply-chain

面向供应链团队,指导如何将机器学习用于预测、分类、优化和运营决策。内容涵盖问题定义、数据评估、模型开发,以及部署和监控规划。

主要功能

  1. 01梳理监督与非监督学习问题
  2. 02指导需求预测和延误分类
  3. 03涵盖聚类、计算机视觉与 NLP
  4. 04提供模型部署和 MLOps 指引

适用场景

  1. 01构建供应链需求预测模型
  2. 02预测运输延误或质量缺陷
  3. 03进行客户分群与运营异常检测
  4. 04规划模型上线和持续监控

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill ml-supply-chain

安装后在 Claude Code 中输入/ml-supply-chain确认已加载。

详细介绍

供应链机器学习

你是将机器学习和人工智能应用于供应链问题的专家。你的目标是帮助构建、训练和部署机器学习模型,以改进供应链运营中的预测、分类、优化和决策。

初步评估

在将机器学习应用于供应链问题之前,请先了解:

  1. 业务问题

    • 需要解决什么问题?(需求预测、质量预测、路线优化)
    • 机器学习模型将支持哪些决策?
    • 当前采用什么方法,有哪些局限?
    • 预期改进和投资回报率是多少?
  2. 数据可用性

    • 有哪些可用数据?(结构化数据、非结构化数据、图像、文本)
    • 历史数据量有多少?(机器学习通常需要 1000 个以上样本)
    • 数据质量如何?(缺失值、异常值、噪声)
    • 是否具备可用特征?(预测变量)
    • 能否访问实时数据?
  3. 机器学习问题类型

    • 监督学习?(有可用的标注数据)
    • 无监督学习?(聚类、异常检测)
    • 强化学习?(序列决策)
    • 时间序列预测?
  4. 技术环境

    • 团队是否具备机器学习专业知识?
    • 计算资源如何?(CPU、GPU、云)
    • 部署环境如何?(批处理、实时 API、边缘)
    • 是否具备 MLOps 能力?

供应链中的机器学习问题类型

监督学习

回归(连续输出)

  • 需求预测
  • 交货时间预测
  • 价格优化
  • 库存水平预测
  • 交付时间估算

分类(类别输出)

  • 产品分类
  • 供应商风险分类
  • 质量缺陷检测
  • 货运延误预测(准时与延误)
  • 客户流失预测
无监督学习

聚类

  • 客户细分
  • 产品分组
  • 路线聚类
  • 运营异常检测

降维

  • 特征提取
  • 数据可视化
  • 降噪
强化学习

序列决策

  • 库存控制策略
  • 动态定价
  • 仓库机器人导航
  • 订单调度

使用机器学习进行需求预测

需求预测的特征工程
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

def create_demand_features(df, target_col='demand'):
    """
    Create comprehensive feature set for demand forecasting

    df: DataFrame with columns ['date', 'sku', 'demand']
    """

    df = df.copy()
    df['date'] = pd.to_datetime(df['date'])
    df = df.sort_values(['sku', 'date'])

    # Time-based features
    df['year'] = df['date'].dt.year
    df['month'] = df['date'].dt.month
    df['day'] = df['date'].dt.day
    df['day_of_week'] = df['date'].dt.dayofweek
    df['day_of_year'] = df['date'].dt.dayofyear
    df['week_of_year'] = df['date'].dt.isocalendar().week
    df['quarter'] = df['date'].dt.quarter
    df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)
    df['is_month_start'] = df['date'].dt.is_month_start.astype(int)
    df['is_month_end'] = df['date'].dt.is_month_end.astype(int)

    # Lag features (previous demand)
    for lag in [1, 7, 14, 28, 365]:
        df[f'lag_{lag}'] = df.groupby('sku')[target_col].shift(lag)

    # Rolling statistics
    for window in [7, 14, 28, 90]:
        df[f'rolling_mean_{window}'] = (
            df.groupby('sku')[target_col]
            .rolling(window, min_periods=1)
            .mean()
            .reset_index(0, drop=True)
        )

        df[f'rolling_std_{window}'] = (
            df.groupby('sku')[target_col]
            .rolling(window, min_periods=1)
            .std()
            .reset_index(0, drop=True)
        )

        df[f'rolling_min_{window}'] = (
            df.groupby('sku')[target_col]
            .rolling(window, min_periods=1)
            .min()
            .reset_index(0, drop=True)
        )

        df[f'rolling_max_{window}'] = (
            df.groupby('sku')[target_col]
            .rolling(window, min_periods=1)
            .max()
            .reset_index(0, drop=True)
        )

    # Exponential weighted moving average
    df['ewm_7'] = (
        df.groupby('sku')[target_col]
        .transform(lambda x: x.ewm(span=7).mean())
    )

    df['ewm_28'] = (
        df.groupby('sku')[target_col]
        .transform(lambda x: x.ewm(span=28).mean())
    )

    # Trend features
    df['demand_trend_7'] = df[f'lag_7'] - df[f'lag_14']
    df['demand_trend_28'] = df[f'lag_28'] - df[f'lag_56']

    # Cyclical encoding for seasonal features
    df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
    df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)
    df['day_sin'] = np.sin(2 * np.pi * df['day_of_week'] / 7)
    df['day_cos'] = np.cos(2 * np.pi * df['day_of_week'] / 7)

    return df

# Example usage
demand_df = pd.DataFrame({
    'date': pd.date_range('2022-01-01', '2024-12-31', freq='D'),
    'sku': 'SKU_001',
    'demand': np.random.poisson(100, 1096)
})

features_df = create_demand_features(demand_df)
print(f"Features created: {len(features_df.columns)} columns")
使用梯度提升进行需求预测
import xgboost as xgb
import lightgbm as lgb
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_error, mean_squared_error
import matplotlib.pyplot as plt

class MLDemandForecaster:
    """
    Machine learning demand forecasting using gradient boosting
    """

    def __init__(self, model_type='xgboost'):
        self.model_type = model_type
        self.model = None
        self.feature_importance = None

    def prepare_data(self, df, target_col='demand', test_size=90):
        """
        Prepare train/test split for time series

        test_size: number of days to hold out for testing
        """

        # Remove rows with NaN (due to lag features)
        df_clean = df.dropna()

        # Identify feature columns (exclude date, sku, target)
        feature_cols = [col for col in df_clean.columns
                       if col not in ['date', 'sku', target_col]]

        # Time-based split
        split_date = df_clean['date'].max() - timedelta(days=test_size)

        train = df_clean[df_clean['date'] <= split_date]
        test = df_clean[df_clean['date'] > split_date]

        X_train = train[feature_cols]
        y_train = train[target_col]
        X_test = test[feature_cols]
        y_test = test[target_col]

        return X_train, X_test, y_train, y_test, feature_cols

    def train(self, X_train, y_train, params=None):
        """
        Train gradient boosting model
        """

        if self.model_type == 'xgboost':
            default_params = {
                'objective': 'reg:squarederror',
                'n_estimators': 1000,
                'learning_rate': 0.05,
                'max_depth': 6,
                'subsample': 0.8,
                'colsample_bytree': 0.8,
                'random_state': 42,
                'early_stopping_rounds': 50
            }

            if params:
                default_params.update(params)

            self.model = xgb.XGBRegressor(**default_params)

        elif self.model_type == 'lightgbm':
            default_params = {
                'objective': 'regression',
                'n_estimators': 1000,
                'learning_rate': 0.05,
                'num_leaves': 31,
                'feature_fraction': 0.8,
                'bagging_fraction': 0.8,
                'bagging_freq': 5,
                'random_state': 42
            }

            if params:
                default_params.update(params)

            self.model = lgb.LGBMRegressor(**default_params)

        # Train
        self.model.fit(
            X_train, y_train,
            eval_set=[(X_train, y_train)],
            verbose=100
        )

        # Store feature importance
        self.feature_importance = pd.DataFrame({
            'feature': X_train.columns,
            'importance': self.model.feature_importances_
        }).sort_values('importance', ascending=False)

    def predict(self, X):
        """Make predictions"""
        return self.model.predict(X)

    def evaluate(self, X_test, y_test):
        """
        Evaluate model performance
        """

        y_pred = self.predict(X_test)

        metrics = {
            'MAE': mean_absolute_error(y_test, y_pred),
            'RMSE': np.sqrt(mean_squared_error(y_test, y_pred)),
            'MAPE': np.mean(np.abs((y_test - y_pred) / y_test)) * 100,
            'Bias': np.mean(y_pred - y_test)
        }

        return metrics, y_pred

    def plot_results(self, y_test, y_pred, dates=None):
        """
        Visualize predictions vs actuals
        """

        fig, axes = plt.subplots(2, 1, figsize=(14, 8))

        # Time series plot
        if dates is not None:
            axes[0].plot(dates, y_test.values, label='Actual', linewidth=2)
            axes[0].plot(dates, y_pred, label='Predicted', linewidth=2, alpha=0.7)
        else:
            axes[0].plot(y_test.values, label='Actual', linewidth=2)
            axes[0].plot(y_pred, label='Predicted', linewidth=2, alpha=0.7)

        axes[0].set_title('Demand Forecast: Actual vs Predicted')
        axes[0].set_xlabel('Time')
        axes[0].set_ylabel('Demand')
        axes[0].legend()
        axes[0].grid(True, alpha=0.3)

        # Scatter plot
        axes[1].scatter(y_test, y_pred, alpha=0.5)
        axes[1].plot([y_test.min(), y_test.max()],
                     [y_test.min(), y_test.max()],
                     'r--', linewidth=2, label='Perfect Prediction')
        axes[1].set_xlabel('Actual Demand')
        axes[1].set_ylabel('Predicted Demand')
        axes[1].set_title('Prediction Accuracy')
        axes[1].legend()
        axes[1].grid(True, alpha=0.3)

        plt.tight_layout()
        plt.show()

    def plot_feature_importance(self, top_n=20):
        """
        Plot feature importance
        """

        top_features = self.feature_importance.head(top_n)

        plt.figure(figsize=(10, 8))
        plt.barh(range(len(top_features)), top_features['importance'])
        plt.yticks(range(len(top_features)), top_features['feature'])
        plt.xlabel('Importance')
        plt.title(f'Top {top_n} Feature Importance')
        plt.gca().invert_yaxis()
        plt.tight_layout()
        plt.show()

# Example usage
forecaster = MLDemandForecaster(model_type='xgboost')

# Prepare data
X_train, X_test, y_train, y_test, feature_cols = forecaster.prepare_data(
    features_df,
    test_size=90
)

# Train model
forecaster.train(X_train, y_train)

# Evaluate
metrics, y_pred = forecaster.evaluate(X_test, y_test)

print("\nModel Performance:")
for metric, value in metrics.items():
    print(f"{metric}: {value:.2f}")

# Visualize
test_dates = features_df[features_df['date'] > features_df['date'].max() - timedelta(days=90)]['date']
forecaster.plot_results(y_test, y_pred, dates=test_dates)
forecaster.plot_feature_importance(top_n=20)
使用 LSTM 进行深度学习
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.preprocessing import StandardScaler

class LSTMForecaster:
    """
    LSTM neural network for demand forecasting
    """

    def __init__(self, lookback=30, forecast_horizon=7):
        """
        lookback: number of historical time steps to use
        forecast_horizon: number of future steps to predict
        """
        self.lookback = lookback
        self.forecast_horizon = forecast_horizon
        self.model = None
        self.scaler = StandardScaler()

    def create_sequences(self, data, target):
        """
        Create sequences for LSTM training

        data: feature array
        target: target array
        """
        X, y = [], []

        for i in range(len(data) - self.lookback - self.forecast_horizon + 1):
            X.append(data[i:i + self.lookback])
            y.append(target[i + self.lookback:i + self.lookback + self.forecast_horizon])

        return np.array(X), np.array(y)

    def build_model(self, n_features):
        """
        Build LSTM architecture
        """

        model = keras.Sequential([
            # First LSTM layer
            layers.LSTM(128, activation='relu',
                       return_sequences=True,
                       input_shape=(self.lookback, n_features)),
            layers.Dropout(0.2),

            # Second LSTM layer
            layers.LSTM(64, activation='relu',
                       return_sequences=True),
            layers.Dropout(0.2),

            # Third LSTM layer
            layers.LSTM(32, activation='relu'),
            layers.Dropout(0.2),

            # Dense layers
            layers.Dense(32, activation='relu'),
            layers.Dense(self.forecast_horizon)
        ])

        model.compile(
            optimizer=keras.optimizers.Adam(learning_rate=0.001),
            loss='mse',
            metrics=['mae']
        )

        return model

    def train(self, train_data, train_target, val_split=0.2, epochs=100, batch_size=32):
        """
        Train LSTM model
        """

        # Scale data
        train_data_scaled = self.scaler.fit_transform(train_data)

        # Create sequences
        X_seq, y_seq = self.create_sequences(train_data_scaled, train_target)

        print(f"Sequence shape: X={X_seq.shape}, y={y_seq.shape}")

        # Build model
        self.model = self.build_model(n_features=train_data.shape[1])

        # Callbacks
        early_stopping = keras.callbacks.EarlyStopping(
            monitor='val_loss',
            patience=15,
            restore_best_weights=True
        )

        reduce_lr = keras.callbacks.ReduceLROnPlateau(
            monitor='val_loss',
            factor=0.5,
            patience=5,
            min_lr=0.00001
        )

        # Train
        history = self.model.fit(
            X_seq, y_seq,
            validation_split=val_split,
            epochs=epochs,
            batch_size=batch_size,
            callbacks=[early_stopping, reduce_lr],
            verbose=1
        )

        return history

    def predict(self, data):
        """
        Make predictions
        """

        # Scale data
        data_scaled = self.scaler.transform(data)

        # Create sequences
        X_seq, _ = self.create_sequences(
            data_scaled,
            np.zeros(len(data))  # Dummy target
        )

        # Predict
        predictions = self.model.predict(X_seq)

        return predictions

# Example usage
lstm_forecaster = LSTMForecaster(lookback=30, forecast_horizon=7)

# Prepare data for LSTM
feature_cols = [col for col in features_df.columns
               if col not in ['date', 'sku', 'demand']]

train_data = features_df[feature_cols].values[:900]
train_target = features_df['demand'].values[:900]

# Train
history = lstm_forecaster.train(train_data, train_target, epochs=50)

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['mae'], label='Training MAE')
plt.plot(history.history['val_mae'], label='Validation MAE')
plt.title('Model MAE')
plt.xlabel('Epoch')
plt.ylabel('MAE')
plt.legend()

plt.tight_layout()
plt.show()

分类问题

货运延误预测
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score
import seaborn as sns

class ShipmentDelayPredictor:
    """
    Predict whether shipment will be delayed
    """

    def __init__(self):
        self.model = None
        self.feature_cols = None

    def prepare_features(self, shipments_df):
        """
        Create features for delay prediction

        shipments_df: columns = ['shipment_id', 'origin', 'destination',
                                 'carrier', 'mode', 'distance', 'weight',
                                 'weather_conditions', 'ship_date', 'promised_date',
                                 'actual_delivery_date']
        """

        df = shipments_df.copy()

        # Create target: delayed = 1, on-time = 0
        df['delayed'] = (df['actual_delivery_date'] > df['promised_date']).astype(int)

        # Temporal features
        df['ship_day_of_week'] = df['ship_date'].dt.dayofweek
        df['ship_month'] = df['ship_date'].dt.month
        df['is_weekend'] = (df['ship_day_of_week'] >= 5).astype(int)

        # Transit features
        df['promised_transit_days'] = (df['promised_date'] - df['ship_date']).dt.days
        df['distance_per_day'] = df['distance'] / df['promised_transit_days']

        # Weight categories
        df['weight_category'] = pd.cut(
            df['weight'],
            bins=[0, 1000, 5000, 15000, float('inf')],
            labels=['light', 'medium', 'heavy', 'very_heavy']
        )

        # Carrier historical performance (would need historical data)
        # For demo, create dummy
        df['carrier_reliability'] = df['carrier'].map({
            'Carrier_A': 0.95,
            'Carrier_B': 0.88,
            'Carrier_C': 0.92
        }).fillna(0.90)

        # Weather conditions (categorical)
        df['bad_weather'] = df['weather_conditions'].isin(['rain', 'snow', 'storm']).astype(int)

        # One-hot encode categorical variables
        df_encoded = pd.get_dummies(
            df,
            columns=['origin', 'destination', 'carrier', 'mode', 'weight_category'],
            drop_first=True
        )

        return df_encoded

    def train(self, X_train, y_train):
        """
        Train random forest classifier
        """

        self.feature_cols = X_train.columns

        self.model = RandomForestClassifier(
            n_estimators=200,
            max_depth=10,
            min_samples_split=20,
            min_samples_leaf=10,
            class_weight='balanced',  # Handle imbalanced classes
            random_state=42,
            n_jobs=-1
        )

        self.model.fit(X_train, y_train)

        # Feature importance
        self.feature_importance = pd.DataFrame({
            'feature': self.feature_cols,
            'importance': self.model.feature_importances_
        }).sort_values('importance', ascending=False)

    def predict(self, X_test):
        """
        Predict delay probability
        """
        return self.model.predict(X_test)

    def predict_proba(self, X_test):
        """
        Predict delay probability
        """
        return self.model.predict_proba(X_test)[:, 1]

    def evaluate(self, X_test, y_test):
        """
        Evaluate classifier performance
        """

        y_pred = self.predict(X_test)
        y_pred_proba = self.predict_proba(X_test)

        print("Classification Report:")
        print(classification_report(y_test, y_pred))

        print(f"\nROC-AUC Score: {roc_auc_score(y_test, y_pred_proba):.3f}")

        # Confusion matrix
        cm = confusion_matrix(y_test, y_pred)

        plt.figure(figsize=(8, 6))
        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
        plt.title('Confusion Matrix')
        plt.ylabel('Actual')
        plt.xlabel('Predicted')
        plt.show()

        return {
            'accuracy': (y_pred == y_test).mean(),
            'roc_auc': roc_auc_score(y_test, y_pred_proba)
        }

# Example usage
delay_predictor = ShipmentDelayPredictor()

# Prepare data
shipments_encoded = delay_predictor.prepare_features(shipments_df)

# Split
feature_cols = [col for col in shipments_encoded.columns
               if col not in ['shipment_id', 'ship_date', 'promised_date',
                             'actual_delivery_date', 'delayed']]

X = shipments_encoded[feature_cols]
y = shipments_encoded['delayed']

# Train/test split
split_idx = int(len(X) * 0.8)
X_train, X_test = X[:split_idx], X[split_idx:]
y_train, y_test = y[:split_idx], y[split_idx:]

# Train
delay_predictor.train(X_train, y_train)

# Evaluate
metrics = delay_predictor.evaluate(X_test, y_test)

聚类与细分

客户细分
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

class CustomerSegmentation:
    """
    Segment customers using clustering
    """

    def __init__(self, n_clusters=4):
        self.n_clusters = n_clusters
        self.scaler = StandardScaler()
        self.pca = PCA(n_components=2)
        self.kmeans = None

    def create_customer_features(self, transactions_df):
        """
        Create RFM and other features for segmentation

        transactions_df: columns = ['customer_id', 'order_date',
                                    'order_value', 'quantity']
        """

        # Calculate RFM metrics
        reference_date = transactions_df['order_date'].max()

        rfm = transactions_df.groupby('customer_id').agg({
            'order_date': lambda x: (reference_date - x.max()).days,  # Recency
            'customer_id': 'count',  # Frequency
            'order_value': 'sum'  # Monetary
        })

        rfm.columns = ['recency', 'frequency', 'monetary']

        # Additional features
        customer_features = transactions_df.groupby('customer_id').agg({
            'order_value': ['mean', 'std', 'min', 'max'],
            'quantity': ['mean', 'sum']
        })

        customer_features.columns = [
            'avg_order_value', 'std_order_value', 'min_order_value', 'max_order_value',
            'avg_quantity', 'total_quantity'
        ]

        # Combine
        features = rfm.join(customer_features)

        return features

    def fit(self, features_df):
        """
        Fit clustering model
        """

        # Scale features
        features_scaled = self.scaler.fit_transform(features_df)

        # Fit K-means
        self.kmeans = KMeans(
            n_clusters=self.n_clusters,
            random_state=42,
            n_init=10
        )

        clusters = self.kmeans.fit_predict(features_scaled)

        # Add cluster labels
        features_df['cluster'] = clusters

        return features_df

    def visualize_clusters(self, features_df):
        """
        Visualize clusters using PCA
        """

        # Get features without cluster column
        feature_cols = [col for col in features_df.columns if col != 'cluster']
        X = features_df[feature_cols]

        # Scale and apply PCA
        X_scaled = self.scaler.transform(X)
        X_pca = self.pca.fit_transform(X_scaled)

        # Plot
        plt.figure(figsize=(12, 8))

        for cluster in range(self.n_clusters):
            mask = features_df['cluster'] == cluster
            plt.scatter(
                X_pca[mask, 0],
                X_pca[mask, 1],
                label=f'Cluster {cluster}',
                s=100,
                alpha=0.6
            )

        # Plot centroids
        centroids_scaled = self.kmeans.cluster_centers_
        centroids_pca = self.pca.transform(centroids_scaled)

        plt.scatter(
            centroids_pca[:, 0],
            centroids_pca[:, 1],
            marker='X',
            s=500,
            c='red',
            edgecolors='black',
            linewidths=2,
            label='Centroids'
        )

        plt.xlabel('First Principal Component')
        plt.ylabel('Second Principal Component')
        plt.title('Customer Segmentation')
        plt.legend()
        plt.grid(True, alpha=0.3)
        plt.show()

    def profile_clusters(self, features_df):
        """
        Create cluster profiles
        """

        # Calculate mean for each cluster
        profiles = features_df.groupby('cluster').mean()

        # Add cluster size
        profiles['size'] = features_df.groupby('cluster').size()

        # Name clusters based on characteristics
        def name_cluster(row):
            if row['frequency'] > profiles['frequency'].median() and \
               row['monetary'] > profiles['monetary'].median():
                return 'Champions'
            elif row['recency'] < profiles['recency'].median() and \
                 row['frequency'] > profiles['frequency'].median():
                return 'Loyal'
            elif row['recency'] > profiles['recency'].median() and \
                 row['frequency'] < profiles['frequency'].median():
                return 'At Risk'
            else:
                return 'Potential'

        profiles['segment_name'] = profiles.apply(name_cluster, axis=1)

        return profiles

# Example usage
segmentation = CustomerSegmentation(n_clusters=4)

# Create features
customer_features = segmentation.create_customer_features(transactions_df)

# Fit clustering
clustered_customers = segmentation.fit(customer_features)

# Visualize
segmentation.visualize_clusters(clustered_customers)

# Profile clusters
cluster_profiles = segmentation.profile_clusters(clustered_customers)
print(cluster_profiles)

供应链中的计算机视觉

使用 CNN 进行质量检测
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator

class QualityInspectionModel:
    """
    Convolutional Neural Network for quality defect detection
    """

    def __init__(self, image_size=(224, 224), num_classes=2):
        """
        image_size: input image dimensions
        num_classes: number of defect categories (e.g., good, defective)
        """
        self.image_size = image_size
        self.num_classes = num_classes
        self.model = None

    def build_model(self):
        """
        Build CNN architecture
        """

        model = models.Sequential([
            # Convolutional layers
            layers.Conv2D(32, (3, 3), activation='relu',
                         input_shape=(*self.image_size, 3)),
            layers.MaxPooling2D((2, 2)),

            layers.Conv2D(64, (3, 3), activation='relu'),
            layers.MaxPooling2D((2, 2)),

            layers.Conv2D(128, (3, 3), activation='relu'),
            layers.MaxPooling2D((2, 2)),

            layers.Conv2D(128, (3, 3), activation='relu'),
            layers.MaxPooling2D((2, 2)),

            # Flatten and dense layers
            layers.Flatten(),
            layers.Dropout(0.5),
            layers.Dense(512, activation='relu'),
            layers.Dropout(0.5),
            layers.Dense(self.num_classes, activation='softmax')
        ])

        model.compile(
            optimizer='adam',
            loss='categorical_crossentropy',
            metrics=['accuracy']
        )

        return model

    def use_transfer_learning(self, base_model_name='VGG16'):
        """
        Use pre-trained model with transfer learning
        """

        # Load pre-trained model
        if base_model_name == 'VGG16':
            base_model = tf.keras.applications.VGG16(
                input_shape=(*self.image_size, 3),
                include_top=False,
                weights='imagenet'
            )
        elif base_model_name == 'ResNet50':
            base_model = tf.keras.applications.ResNet50(
                input_shape=(*self.image_size, 3),
                include_top=False,
                weights='imagenet'
            )

        # Freeze base model
        base_model.trainable = False

        # Add custom top layers
        model = models.Sequential([
            base_model,
            layers.GlobalAveragePooling2D(),
            layers.Dense(256, activation='relu'),
            layers.Dropout(0.5),
            layers.Dense(self.num_classes, activation='softmax')
        ])

        model.compile(
            optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
            loss='categorical_crossentropy',
            metrics=['accuracy']
        )

        return model

    def train(self, train_dir, val_dir, epochs=50, batch_size=32,
             use_transfer_learning=True):
        """
        Train model on image data

        train_dir: directory with training images organized by class
        val_dir: directory with validation images
        """

        # Data augmentation
        train_datagen = ImageDataGenerator(
            rescale=1./255,
            rotation_range=20,
            width_shift_range=0.2,
            height_shift_range=0.2,
            horizontal_flip=True,
            fill_mode='nearest'
        )

        val_datagen = ImageDataGenerator(rescale=1./255)

        # Load data
        train_generator = train_datagen.flow_from_directory(
            train_dir,
            target_size=self.image_size,
            batch_size=batch_size,
            class_mode='categorical'
        )

        val_generator = val_datagen.flow_from_directory(
            val_dir,
            target_size=self.image_size,
            batch_size=batch_size,
            class_mode='categorical'
        )

        # Build model
        if use_transfer_learning:
            self.model = self.use_transfer_learning()
        else:
            self.model = self.build_model()

        # Callbacks
        early_stopping = tf.keras.callbacks.EarlyStopping(
            monitor='val_loss',
            patience=10,
            restore_best_weights=True
        )

        model_checkpoint = tf.keras.callbacks.ModelCheckpoint(
            'best_quality_model.h5',
            monitor='val_accuracy',
            save_best_only=True
        )

        # Train
        history = self.model.fit(
            train_generator,
            epochs=epochs,
            validation_data=val_generator,
            callbacks=[early_stopping, model_checkpoint]
        )

        return history

    def predict_defects(self, image_path):
        """
        Predict defects in single image
        """

        # Load and preprocess image
        img = tf.keras.preprocessing.image.load_img(
            image_path,
            target_size=self.image_size
        )
        img_array = tf.keras.preprocessing.image.img_to_array(img)
        img_array = np.expand_dims(img_array, axis=0)
        img_array /= 255.0

        # Predict
        predictions = self.model.predict(img_array)

        return predictions[0]

# Example usage
quality_model = QualityInspectionModel(image_size=(224, 224), num_classes=2)

# Train model
# history = quality_model.train(
#     train_dir='data/train',
#     val_dir='data/val',
#     epochs=30,
#     use_transfer_learning=True
# )

# Predict on new image
# predictions = quality_model.predict_defects('path/to/product_image.jpg')
# print(f"Defect probability: {predictions[1]:.2%}")

自然语言处理

从新闻中分析供应商风险
from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
import requests

class SupplierRiskNLP:
    """
    Analyze supplier risk from news articles and reports
    """

    def __init__(self):
        # Load sentiment analysis model
        self.sentiment_analyzer = pipeline(
            "sentiment-analysis",
            model="distilbert-base-uncased-finetuned-sst-2-english"
        )

        # Load NER model for entity extraction
        self.ner_analyzer = pipeline(
            "ner",
            model="dbmdz/bert-large-cased-finetuned-conll03-english",
            aggregation_strategy="simple"
        )

    def fetch_news(self, supplier_name, api_key):
        """
        Fetch news articles about supplier

        Uses News API (newsapi.org)
        """

        url = 'https://newsapi.org/v2/everything'
        params = {
            'q': supplier_name,
            'apiKey': api_key,
            'language': 'en',
            'sortBy': 'publishedAt',
            'pageSize': 100
        }

        response = requests.get(url, params=params)

        if response.status_code == 200:
            articles = response.json()['articles']
            return articles
        else:
            return []

    def analyze_sentiment(self, text):
        """
        Analyze sentiment of text
        """

        # Split long text into chunks (model has token limit)
        max_length = 512
        chunks = [text[i:i+max_length] for i in range(0, len(text), max_length)]

        sentiments = []
        for chunk in chunks:
            result = self.sentiment_analyzer(chunk)[0]
            sentiments.append(result)

        # Aggregate sentiments
        positive_count = sum(1 for s in sentiments if s['label'] == 'POSITIVE')
        avg_score = np.mean([s['score'] for s in sentiments])

        overall = {
            'positive_ratio': positive_count / len(sentiments),
            'average_confidence': avg_score,
            'risk_indicator': 1 - (positive_count / len(sentiments))
        }

        return overall

    def extract_risk_indicators(self, articles):
        """
        Extract risk-related keywords and entities
        """

        risk_keywords = [
            'bankruptcy', 'lawsuit', 'recall', 'defect', 'violation',
            'investigation', 'scandal', 'delay', 'shortage', 'disruption',
            'closure', 'layoff', 'protest', 'strike'
        ]

        risk_score = 0
        risk_events = []

        for article in articles:
            text = article.get('title', '') + ' ' + article.get('description', '')
            text_lower = text.lower()

            # Check for risk keywords
            for keyword in risk_keywords:
                if keyword in text_lower:
                    risk_score += 1
                    risk_events.append({
                        'keyword': keyword,
                        'title': article.get('title'),
                        'date': article.get('publishedAt'),
                        'url': article.get('url')
                    })

        return risk_score, risk_events

    def assess_supplier_risk(self, supplier_name, api_key):
        """
        Complete supplier risk assessment
        """

        print(f"Assessing risk for supplier: {supplier_name}")

        # Fetch news
        articles = self.fetch_news(supplier_name, api_key)
        print(f"Found {len(articles)} articles")

        if not articles:
            return {
                'supplier': supplier_name,
                'risk_level': 'Unknown',
                'score': 0,
                'message': 'No articles found'
            }

        # Analyze sentiment
        all_text = ' '.join([
            art.get('title', '') + ' ' + art.get('description', '')
            for art in articles
        ])

        sentiment = self.analyze_sentiment(all_text)

        # Extract risk indicators
        risk_score, risk_events = self.extract_risk_indicators(articles)

        # Calculate overall risk
        # Higher risk_indicator and higher risk_score = higher risk
        overall_risk_score = (sentiment['risk_indicator'] * 0.6 +
                             min(risk_score / len(articles), 1.0) * 0.4)

        if overall_risk_score > 0.7:
            risk_level = 'HIGH'
        elif overall_risk_score > 0.4:
            risk_level = 'MEDIUM'
        else:
            risk_level = 'LOW'

        return {
            'supplier': supplier_name,
            'risk_level': risk_level,
            'risk_score': overall_risk_score,
            'sentiment_analysis': sentiment,
            'risk_keyword_count': risk_score,
            'risk_events': risk_events[:5],  # Top 5 events
            'articles_analyzed': len(articles)
        }

# Example usage
risk_analyzer = SupplierRiskNLP()

# Assess supplier risk
# assessment = risk_analyzer.assess_supplier_risk('Acme Corp', api_key='your_api_key')
# print(f"Risk Level: {assessment['risk_level']}")
# print(f"Risk Score: {assessment['risk_score']:.2f}")

MLOps 与模型部署

使用 FastAPI 部署模型
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import joblib
import numpy as np
import uvicorn

app = FastAPI(title="Supply Chain ML API")

# Load pre-trained model
demand_model = joblib.load('demand_forecast_model.pkl')
delay_model = joblib.load('delay_prediction_model.pkl')

# Request models
class DemandForecastRequest(BaseModel):
    sku: str
    features: dict  # Feature dictionary

class DelayPredictionRequest(BaseModel):
    shipment_features: dict

# Response models
class DemandForecastResponse(BaseModel):
    sku: str
    predicted_demand: float
    confidence_interval_lower: float
    confidence_interval_upper: float

class DelayPredictionResponse(BaseModel):
    delay_probability: float
    predicted_class: str
    confidence: float

@app.post("/api/v1/forecast/demand", response_model=DemandForecastResponse)
async def forecast_demand(request: DemandForecastRequest):
    """
    Forecast demand for a SKU
    """
    try:
        # Extract features
        features_array = np.array(list(request.features.values())).reshape(1, -1)

        # Predict
        prediction = demand_model.predict(features_array)[0]

        # Calculate confidence interval (simplified)
        std_dev = prediction * 0.15  # Assume 15% std dev
        ci_lower = max(0, prediction - 1.96 * std_dev)
        ci_upper = prediction + 1.96 * std_dev

        return DemandForecastResponse(
            sku=request.sku,
            predicted_demand=float(prediction),
            confidence_interval_lower=float(ci_lower),
            confidence_interval_upper=float(ci_upper)
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/api/v1/predict/delay", response_model=DelayPredictionResponse)
async def predict_delay(request: DelayPredictionRequest):
    """
    Predict shipment delay probability
    """
    try:
        # Extract features
        features_array = np.array(
            list(request.shipment_features.values())
        ).reshape(1, -1)

        # Predict probability
        delay_proba = delay_model.predict_proba(features_array)[0][1]
        predicted_class = "Delayed" if delay_proba > 0.5 else "On-Time"
        confidence = max(delay_proba, 1 - delay_proba)

        return DelayPredictionResponse(
            delay_probability=float(delay_proba),
            predicted_class=predicted_class,
            confidence=float(confidence)
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {"status": "healthy"}

# Run with: uvicorn api:app --reload

工具与库

Python 机器学习库

核心机器学习:

  • scikit-learn:经典机器学习算法
  • xgboost:梯度提升
  • lightgbm:快速梯度提升
  • catboost:面向类别数据的梯度提升

深度学习:

  • tensorflow/keras:神经网络
  • pytorch:动态神经网络
  • fastai:高级深度学习

时间序列:

  • prophet:时间序列预测
  • statsmodels:统计模型
  • sktime:时间序列机器学习
  • darts:时间序列预测库

自然语言处理:

  • transformers (Hugging Face):预训练语言模型
  • spacy:工业级自然语言处理
  • nltk:自然语言工具包

计算机视觉:

  • opencv:计算机视觉
  • PIL/pillow:图像处理
  • albumentations:图像增强

模型部署:

  • fastapi:API 框架
  • flask:Web 框架
  • streamlit:数据应用
  • mlflow:机器学习生命周期管理
  • kubeflow:在 Kubernetes 上进行机器学习
云端机器学习平台

训练与部署:

  • AWS SageMaker:端到端机器学习平台
  • Google Cloud AI Platform:GCP 上的机器学习
  • Azure Machine Learning:Microsoft 机器学习平台
  • Databricks:统一分析平台

MLOps:

  • MLflow:开源机器学习生命周期管理
  • Weights & Biases:实验跟踪
  • Neptune.ai:机器学习元数据存储
  • DVC:数据版本控制

常见挑战与解决方案

挑战:训练数据不足

问题:

  • 机器学习需要大型数据集(1000 个以上样本)
  • 供应链数据通常有限

解决方案:

  • 使用预训练模型进行迁移学习
  • 使用数据增强技术
  • 生成合成数据
  • 从更简单的模型(参数更少)入手
  • 合并多个数据源
  • 使用半监督学习或主动学习
挑战:类别不平衡

问题:

  • 罕见事件(缺陷、延迟)的样本很少
  • 模型偏向多数类

解决方案:

  • 在损失函数中使用类别权重
  • 对少数类过采样(SMOTE)
  • 对多数类欠采样
  • 使用集成方法
  • 采用异常检测方法
  • 使用适当的指标(精确率、召回率、F1,而不只是准确率)
挑战:模型可解释性

问题:

  • 黑盒模型难以解释
  • 业务需要了解预测结果

解决方案:

  • 使用可解释模型(线性模型、基于树的模型)
  • 使用 SHAP 值评估特征重要性
  • 使用 LIME 提供局部解释
  • 绘制部分依赖图
  • 分析特征重要性
  • 编写模型文档并进行验证
挑战:概念漂移

问题:

  • 供应链模式会随时间变化
  • 模型性能会下降

解决方案:

  • 持续监控模型性能
  • 定期重新训练模型(每月、每季度)
  • 采用在线学习方法
  • 使用统计检验检测漂移
  • 对模型更新进行 A/B 测试
  • 维护多个模型版本

输出格式

机器学习项目报告模板

执行摘要:

  • 要解决的业务问题
  • 采用的机器学习方法
  • 关键结果和指标
  • 建议采取的行动

问题定义:

  • 详细的问题陈述
  • 定义成功指标
  • 当前基准性能

数据分析:

  • 数据源和数据量
  • 特征工程方法
  • 数据质量评估
  • 探索性数据分析

模型开发:

模型算法训练准确率测试准确率MAERMSE
基线移动平均--45.262.3
模型 1随机森林0.920.8732.144.5
模型 2XGBoost0.950.8928.339.7
模型 3LSTM0.940.9026.837.2

模型性能:

  • 所选模型及其理由
  • 性能指标
  • 特征重要性
  • 误差分析

部署计划:

  • 部署架构
  • API 规范
  • 监控策略
  • 重新训练计划

业务影响:

  • 预期改进
  • 成本效益分析
  • 实施路线图
  • 风险与缓解措施

要提出的问题

如果需要更多背景信息:

  1. 哪个具体的供应链问题需要机器学习解决?
  2. 有哪些可用数据?(数据量、特征、质量)
  3. 要预测的目标变量或结果是什么?
  4. 这是监督学习、无监督学习还是强化学习?
  5. 当前基准性能如何?
  6. 要实现业务价值需要达到什么准确率?
  7. 有哪些部署要求?(批处理、实时、边缘端)
  8. 团队具备哪些机器学习专业知识?

相关技能

  • demand-forecasting:用于时间序列预测方法
  • supply-chain-analytics:用于 KPI 和指标
  • optimization-modeling:用于将机器学习与优化相结合
  • prescriptive-analytics:用于基于机器学习的决策支持
  • digital-twin-modeling:用于结合机器学习进行仿真
  • computer-vision-warehouse:用于仓库计算机视觉应用
  • nlp-supply-chain:用于自然语言处理相关应用
  • neural-networks-forecasting:用于深度学习预测

评论

支持 Markdown