Deeplearning4j+ Spring Boot 电商用户复购预测案例

一、项目概述

什么是复购预测?

复购预测是电商平台的核心需求之一,简单来说就是通过分析用户的历史行为数据,预测用户未来一段时间内是否会再次购买商品。这对于电商平台的运营策略制定非常重要:

  • 对高概率复购的用户,可以推送优惠券或个性化推荐,促进其再次购买
  • 对低概率复购的用户,可以采取召回营销措施,防止用户流失

技术栈介绍

本项目使用了以下技术栈:

  • Spring Boot 3.2:提供快速开发框架和REST接口
  • JDK 17:最新的长期支持版本,兼容性好
  • DL4J (DeepLearning4J):Java语言的深度学习框架,适合在JVM环境中运行
  • ND4J :DL4J的数值计算核心,提供高效的矩阵运算

二、DL4J 核心概念与 API 说明

1. 神经网络基础概念

神经网络

神经网络就像一个模拟人脑思考的数学模型,由多个"神经元"组成。在本项目中,我们使用了一个三层的神经网络:

  • 输入层:接收用户行为数据(如购买次数、消费金额等)
  • 隐藏层:提取和学习数据中的规律
  • 输出层:输出预测结果(复购概率)
核心 API:MultiLayerNetwork
java 复制代码
// 创建和初始化神经网络模型
MultiLayerNetwork model = new MultiLayerNetwork(config);
model.init();

作用MultiLayerNetwork是DL4J中表示多层神经网络的核心类,它封装了整个神经网络的结构和参数。
为什么需要:它提供了模型训练、预测等核心功能,是整个深度学习流程的核心。

激活函数

激活函数就像神经元的"开关",决定神经元是否被激活。本项目使用了两种激活函数:

  • ReLU:在隐藏层使用,能有效处理非线性关系
  • Sigmoid:在输出层使用,将结果压缩到0-1之间,表示复购概率
核心 API:Activation
java 复制代码
// 在隐藏层使用ReLU激活函数
.activation(Activation.RELU)

// 在输出层使用Sigmoid激活函数
.activation(Activation.SIGMOID)

作用Activation类提供了各种激活函数的实现。
为什么需要:激活函数能帮助神经网络学习复杂的非线性关系,是神经网络能够解决复杂问题的关键。

损失函数

损失函数 就像一个"评分器",衡量模型预测的准确性。本项目使用了二分类交叉熵损失函数,专门用于判断"是"或"否"的问题。

核心 API:LossFunctions
java 复制代码
// 使用二分类交叉熵损失函数
new OutputLayer.Builder(LossFunctions.LossFunction.BINARY_CROSSENTROPY)

作用LossFunctions类提供了各种损失函数的实现。
为什么需要:损失函数是模型训练的目标,它告诉模型预测结果与真实结果的差距有多大,是模型参数更新的依据。

优化器

优化器 就像一个"教练",指导模型如何调整参数以提高预测准确率。本项目使用了Adam优化器,它能自适应地调整学习率,使模型训练更高效。

核心 API:Adam
java 复制代码
// 使用Adam优化器,学习率为0.001
.updater(new Adam(LEARNING_RATE))

作用Adam是一种常用的优化器实现。
为什么需要:优化器决定了模型如何根据损失函数的结果调整参数,好的优化器能使模型训练更快、更稳定。

2. 数据处理相关概念

DataSet

DataSet是DL4J中的"数据表格",包含特征数据和标签数据,是模型能够理解的数据格式。

核心 API:DataSet
java 复制代码
// 创建数据集,包含特征和标签
DataSet allData = new DataSet(features, labels);

作用DataSet类用于封装模型训练和测试数据。
为什么需要:它提供了一种统一的数据格式,方便模型进行批量处理和训练。

数据标准化

数据标准化就像"统一度量衡",将不同范围的特征(如金额8000和次数15)缩放到同一范围(均值0,方差1),使模型训练更稳定。

核心 API:NormalizerStandardize
java 复制代码
// 创建标准化器
private final NormalizerStandardize normalizer = new NormalizerStandardize();

// 用训练数据拟合标准化器
normalizer.fit(trainData);

// 对数据进行标准化转换
normalizer.transform(normalizedData);

作用NormalizerStandardize类用于对数据进行标准化处理。
为什么需要:标准化能消除特征之间的量纲差异,使模型训练更稳定,提高模型的收敛速度和预测准确率。

3. 模型训练与预测相关概念

模型训练

模型训练是指通过大量数据让模型学习数据中的规律,调整模型参数以提高预测准确率的过程。

核心 API:fit()
java 复制代码
// 训练模型
repurchasePredictModel.fit(trainData);

作用fit()方法用于训练模型。
为什么需要:它是模型学习数据规律的核心方法,通过多次调用,模型能逐渐提高预测准确率。

模型预测

模型预测是指使用训练好的模型对新数据进行预测的过程。

核心 API:output()
java 复制代码
// 模型预测
INDArray predictResult = repurchasePredictModel.output(normalizedFeature);
double repurchaseProb = predictResult.getDouble(0);

作用output()方法用于使用模型进行预测。
为什么需要:它是模型应用的核心方法,能根据输入数据生成预测结果。

三、项目代码详解

1. 项目结构

复制代码
dl4j-springboot-ecommerce-demo/
├── pom.xml                          // 依赖配置
├── src/main/java/com/ecommerce/dl4j/
│   ├── Dl4jEcommerceDemoApplication.java  // 启动类
│   ├── config/
│   │   └── Dl4jModelConfig.java           // 模型配置类
│   ├── controller/
│   │   └── UserRepurchaseController.java  // 预测接口控制器
│   ├── model/
│   │   └── UserBehaviorDTO.java           // 用户行为数据模型
│   ├── service/
│   │   ├── UserRepurchaseService.java     // 预测服务接口
│   │   └── impl/
│   │       └── UserRepurchaseServiceImpl.java  // 服务实现类
│   └── util/
│       └── DataNormalizerUtil.java        // 数据标准化工具类
└── src/main/resources/
    └── application.properties           // 配置文件

2. 核心代码详解

2.1 模型配置类 (Dl4jModelConfig.java)
java 复制代码
@Configuration
public class Dl4jModelConfig {
    // 核心参数(电商场景:5个用户行为特征)
    private static final int INPUT_FEATURES = 5;  // 输入特征数
    private static final long RANDOM_SEED = 42;   // 随机种子(结果可复现)
    private static final double LEARNING_RATE = 0.001; // 学习率

    @Bean
    public MultiLayerNetwork repurchasePredictModel() {
        // 1. 定义模型配置(核心:层结构)
        MultiLayerConfiguration config = new NeuralNetConfiguration.Builder()
                .seed(RANDOM_SEED)                          // 固定随机种子
                .updater(new Adam(LEARNING_RATE))            // 优化器:Adam
                .l2(0.001)                                  // L2正则化:防止过拟合
                .list()                                     // 顺序层结构
                // 第1层:输入层 + 隐藏层1(5个特征 → 32个神经元)
                .layer(0, new DenseLayer.Builder()
                        .nIn(INPUT_FEATURES)                // 输入特征数
                        .nOut(32)                           // 输出神经元数
                        .activation(Activation.RELU)        // 激活函数:ReLU
                        .weightInit(WeightInit.XAVIER)      // 权重初始化
                        .build()
                )
                // 第2层:隐藏层2(32 → 16神经元)
                .layer(1, new DenseLayer.Builder()
                        .nIn(32)
                        .nOut(16)
                        .activation(Activation.RELU)
                        .weightInit(WeightInit.XAVIER)
                        .build()
                )
                // 第3层:输出层(16 → 1神经元,二分类)
                .layer(2, new OutputLayer.Builder(LossFunctions.LossFunction.BINARY_CROSSENTROPY)
                        .nIn(16)
                        .nOut(1)                            // 二分类输出1个值(0-1概率)
                        .activation(Activation.SIGMOID)     // Sigmoid:输出0-1概率
                        .weightInit(WeightInit.XAVIER)
                        .build()
                )
                .build();

        // 2. 初始化模型
        MultiLayerNetwork model = new MultiLayerNetwork(config);
        model.init();

        System.out.println("===== DL4J复购预测模型初始化完成 =====");
        return model;
    }
}

代码解析

  • @Configuration:Spring注解,标记这是一个配置类
  • @Bean:Spring注解,将方法返回的对象注册为Spring容器中的Bean
  • NeuralNetConfiguration.Builder:用于构建神经网络配置
  • DenseLayer.Builder:用于构建全连接层
  • OutputLayer.Builder:用于构建输出层
  • MultiLayerNetwork:多层神经网络模型

为什么这样写

  • 我们使用了三层神经网络,因为对于复购预测这样的问题,三层网络已经足够复杂,能够捕捉到数据中的复杂关系
  • 隐藏层神经元数量逐渐减少(32→16),这样可以逐渐提取更高级的特征
  • 输出层使用Sigmoid激活函数,因为它能将输出压缩到0-1之间,适合表示概率
  • 使用Adam优化器,因为它在实践中表现良好,能自适应调整学习率
2.2 服务实现类 (UserRepurchaseServiceImpl.java)
java 复制代码
@Service
public class UserRepurchaseServiceImpl implements UserRepurchaseService {

    // 核心参数
    private static final int DATA_SIZE = 1000;    // 模拟电商用户数据量
    private static final int BATCH_SIZE = 32;     // 批次大小
    private static final int EPOCHS = 50;        // 训练轮数
    private static final int INPUT_FEATURES = 5;  // 输入特征数
    private static final double THRESHOLD = 0.5;  // 复购概率阈值(>0.5=高概率)

    // 注入DL4J模型和工具类
    @Autowired
    private MultiLayerNetwork repurchasePredictModel;
    @Autowired
    private DataNormalizerUtil dataNormalizerUtil;

    /**
     * 项目启动后自动训练模型(PostConstruct:初始化完成后执行)
     */
    @PostConstruct
    @Override
    public void trainModel() {
        try {
            System.out.println("===== 开始训练电商用户复购预测模型 =====");

            // 步骤1:模拟电商用户行为数据
            Random random = new Random(42);
            List<double[]> featuresList = new ArrayList<>();
            List<double[]> labelsList = new ArrayList<>();

            for (int i = 0; i < DATA_SIZE; i++) {
                // 生成5个用户行为特征
                int purchaseCount = random.nextInt(20) + 1;          // 购买次数:1-20次
                double totalAmount = random.nextDouble() * 10000;    // 消费金额:0-10000元
                int lastPurchaseDays = random.nextInt(60);           // 最近购买:0-60天
                int browseTimes = random.nextInt(100);               // 浏览次数:0-100次
                int collectCount = random.nextInt(30);               // 收藏数:0-30个

                // 生成标签(是否复购:1=是,0=否)
                double repurchaseProb = 0.1 +
                        (purchaseCount / 20.0) * 0.3 +    // 购买次数占30%权重
                        (totalAmount / 10000.0) * 0.2 +   // 消费金额占20%权重
                        (1 - lastPurchaseDays / 60.0) * 0.25 +  // 最近购买占25%权重
                        (browseTimes / 100.0) * 0.15 +   // 浏览次数占15%权重
                        (collectCount / 30.0) * 0.1;     // 收藏数占10%权重

                int isRepurchase = repurchaseProb > 0.5 ? 1 : 0;

                // 添加到列表
                featuresList.add(new double[]{purchaseCount, totalAmount, lastPurchaseDays, browseTimes, collectCount});
                labelsList.add(new double[]{isRepurchase});
            }

            // 步骤2:转换为DL4J的DataSet格式
            double[][] featuresArray = featuresList.toArray(new double[0][]);
            double[][] labelsArray = labelsList.toArray(new double[0][]);
            INDArray features = Nd4j.create(featuresArray);
            INDArray labels = Nd4j.create(labelsArray);
            DataSet allData = new DataSet(features, labels);

            // 步骤3:数据标准化
            dataNormalizerUtil.fit(features);
            INDArray normalizedFeatures = dataNormalizerUtil.transform(features);
            DataSet normalizedData = new DataSet(normalizedFeatures, labels);

            // 步骤4:拆分训练集和测试集
            normalizedData.shuffle(42);
            DataSet trainData = normalizedData.splitTestAndTrain(0.8).getTrain();
            DataSet testData = normalizedData.splitTestAndTrain(0.8).getTest();

            // 步骤5:训练模型
            for (int i = 0; i < EPOCHS; i++) {
                repurchasePredictModel.fit(trainData);

                // 每10轮打印训练损失
                if ((i + 1) % 10 == 0) {
                    // 手动计算训练损失
                    INDArray trainFeatures = trainData.getFeatures();
                    INDArray trainLabels = trainData.getLabels();
                    INDArray trainPredictions = repurchasePredictModel.output(trainFeatures);
                    
                    // 计算二分类交叉熵损失
                    double trainLoss = 0.0;
                    for (int j = 0; j < trainFeatures.size(0); j++) {
                        double prediction = trainPredictions.getDouble(j, 0);
                        double label = trainLabels.getDouble(j, 0);
                        // 避免log(0)的情况
                        prediction = Math.max(prediction, 1e-10);
                        prediction = Math.min(prediction, 1 - 1e-10);
                        trainLoss -= (label * Math.log(prediction) + (1 - label) * Math.log(1 - prediction));
                    }
                    trainLoss /= trainFeatures.size(0);
                    
                    System.out.println("第" + (i + 1) + "轮训练 → 损失值:" + String.format("%.4f", trainLoss));
                }
            }

            // 步骤6:评估模型
            // 手动计算测试集损失和准确率
            INDArray testFeatures = testData.getFeatures();
            INDArray testLabels = testData.getLabels();
            INDArray testPredictions = repurchasePredictModel.output(testFeatures);
            
            // 计算二分类交叉熵损失
            double testLoss = 0.0;
            int correctPredictions = 0;
            
            for (int i = 0; i < testFeatures.size(0); i++) {
                double prediction = testPredictions.getDouble(i, 0);
                double label = testLabels.getDouble(i, 0);
                
                // 计算损失
                prediction = Math.max(prediction, 1e-10);
                prediction = Math.min(prediction, 1 - 1e-10);
                testLoss -= (label * Math.log(prediction) + (1 - label) * Math.log(1 - prediction));
                
                // 计算准确率
                int predictedClass = prediction > THRESHOLD ? 1 : 0;
                int actualClass = (int) label;
                if (predictedClass == actualClass) {
                    correctPredictions++;
                }
            }
            
            testLoss /= testFeatures.size(0);
            double testAccuracy = (double) correctPredictions / testFeatures.size(0);
            
            System.out.println("===== 模型训练完成 =====");
            System.out.println("测试集损失值:" + String.format("%.4f", testLoss));
            System.out.println("测试集准确率:" + String.format("%.2f%%", testAccuracy * 100));

        } catch (Exception e) {
            System.err.println("模型训练失败:" + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 预测用户复购概率
     */
    @Override
    public UserBehaviorDTO predictRepurchase(UserBehaviorDTO userBehavior) {
        try {
            // 步骤1:将DTO转换为特征数组
            double[] features = new double[]{
                    userBehavior.getPurchaseCount(),
                    userBehavior.getTotalAmount(),
                    userBehavior.getLastPurchaseDays(),
                    userBehavior.getBrowseTimes(),
                    userBehavior.getCollectCount()
            };

            // 步骤2:转换为INDArray并标准化
            INDArray featureArray = Nd4j.create(new double[][]{features});
            INDArray normalizedFeature = dataNormalizerUtil.transform(featureArray);

            // 步骤3:模型预测
            INDArray predictResult = repurchasePredictModel.output(normalizedFeature);
            double repurchaseProb = predictResult.getDouble(0);

            // 步骤4:封装结果
            userBehavior.setRepurchaseProb(repurchaseProb);
            userBehavior.setRepurchaseResult(repurchaseProb > THRESHOLD ? "高概率复购" : "低概率复购");

            return userBehavior;

        } catch (Exception e) {
            System.err.println("预测失败:" + e.getMessage());
            userBehavior.setRepurchaseResult("预测失败");
            return userBehavior;
        }
    }
}

代码解析

  • @Service:Spring注解,标记这是一个服务类
  • @PostConstruct:Spring注解,标记方法在Bean初始化后执行
  • @Autowired:Spring注解,用于自动注入依赖

为什么这样写

  1. 模拟数据生成

    • 我们生成了1000条模拟的电商用户行为数据,包括购买次数、消费金额等特征
    • 标签生成基于一个合理的业务规则:购买次数多、消费金额高、最近购买天数少的用户,复购概率更高
  2. 数据处理

    • 将List<double[]>转换为INDArray,这是DL4J能理解的数据格式
    • 进行数据标准化,消除特征之间的量纲差异
    • 拆分训练集和测试集,用于模型训练和评估
  3. 模型训练

    • 训练50轮,每轮都使用完整的训练数据
    • 每10轮计算并打印训练损失,用于监控训练过程
    • 训练完成后评估模型在测试集上的表现
  4. 模型预测

    • 接收用户行为数据,转换为特征数组
    • 进行数据标准化,确保输入数据与训练数据格式一致
    • 使用模型进行预测,得到复购概率
    • 根据阈值判断用户是否为高概率复购用户
2.3 数据标准化工具类 (DataNormalizerUtil.java)
java 复制代码
@Component
public class DataNormalizerUtil {

    // 标准化器(全局唯一)
    private final NormalizerStandardize normalizer = new NormalizerStandardize();

    /**
     * 初始化标准化器(用训练数据拟合)
     * @param trainData 训练数据矩阵
     */
    public void fit(INDArray trainData) {
        normalizer.fit(trainData);
        System.out.println("===== 数据标准化器初始化完成 =====");
    }

    /**
     * 对数据进行标准化转换
     * @param data 原始数据
     * @return 标准化后的数据
     */
    public INDArray transform(INDArray data) {
        INDArray normalizedData = data.dup(); // 复制避免修改原数据
        normalizer.transform(normalizedData);
        return normalizedData;
    }
}

代码解析

  • @Component:Spring注解,标记这是一个组件类
  • NormalizerStandardize:DL4J提供的标准化器,用于将数据标准化为均值0、方差1

为什么这样写

  • 标准化能消除特征之间的量纲差异,使模型训练更稳定
  • 我们使用同一个标准化器处理训练数据和预测数据,确保数据格式一致
2.4 控制器类 (UserRepurchaseController.java)
java 复制代码
@RestController
@RequestMapping("/api/repurchase")
public class UserRepurchaseController {

    @Autowired
    private UserRepurchaseService userRepurchaseService;

    /**
     * 预测用户复购概率接口
     * 请求示例(JSON):
     * {
     *   "purchaseCount": 15,
     *   "totalAmount": 8000.0,
     *   "lastPurchaseDays": 5,
     *   "browseTimes": 80,
     *   "collectCount": 20
     * }
     * @param userBehavior 用户行为数据
     * @return 预测结果
     */
    @PostMapping("/predict")
    public UserBehaviorDTO predictRepurchase(@RequestBody UserBehaviorDTO userBehavior) {
        System.out.println("===== 接收复购预测请求 =====");
        return userRepurchaseService.predictRepurchase(userBehavior);
    }
}

代码解析

  • @RestController:Spring注解,标记这是一个REST控制器类
  • @RequestMapping:Spring注解,指定请求路径前缀
  • @PostMapping:Spring注解,指定POST请求路径
  • @RequestBody:Spring注解,将请求体转换为Java对象

为什么这样写

  • 提供REST接口,方便外部系统调用
  • 使用POST请求,适合传输复杂的JSON数据
  • 直接返回预测结果,便于调用方处理

四、训练过程与预测流程详解

1. 训练过程

训练流程

  1. 数据准备:生成模拟的电商用户行为数据
  2. 数据转换:将数据转换为DL4J能理解的INDArray格式
  3. 数据标准化:消除特征之间的量纲差异
  4. 数据拆分:将数据拆分为训练集和测试集
  5. 模型训练:使用训练数据训练模型
  6. 模型评估:使用测试数据评估模型性能

训练原理

  • 模型训练的本质是通过调整模型参数,使模型能够更好地拟合训练数据
  • 每轮训练,模型会计算预测结果与真实结果的差距(损失值)
  • 然后使用优化器调整模型参数,减小这个差距
  • 经过多轮训练,模型会逐渐学习到数据中的规律

2. 预测流程

预测流程

  1. 接收请求:接收用户行为数据
  2. 数据转换:将数据转换为INDArray格式
  3. 数据标准化:使用与训练数据相同的标准进行标准化
  4. 模型预测:使用训练好的模型进行预测
  5. 结果处理:根据预测概率判断用户是否为高概率复购用户
  6. 返回结果:返回包含预测结果的用户行为数据

预测原理

  • 模型预测的本质是使用训练时学习到的规律,对新数据进行推断
  • 输入层接收标准化后的特征数据
  • 隐藏层提取特征,进行非线性变换
  • 输出层使用Sigmoid激活函数,将结果压缩到0-1之间,表示复购概率

五、常见问题与解决方案

1. 依赖下载失败

问题 :首次导入项目时,Maven依赖下载失败
解决方案

  • 检查网络连接
  • 配置Maven镜像源,如阿里云镜像
  • 耐心等待,依赖包较大,首次下载可能需要较长时间

2. 模型训练失败

问题 :模型训练过程中出现异常
解决方案

  • 检查JDK版本是否为17
  • 检查DL4J版本是否与代码兼容
  • 检查内存是否足够,训练深度学习模型需要一定的内存

3. 预测结果不准确

问题 :模型预测结果与实际情况不符
解决方案

  • 增加训练数据量
  • 调整模型参数,如隐藏层神经元数量、学习率等
  • 增加训练轮数
  • 添加更多特征,如用户年龄、性别、商品类别偏好等

4. 服务启动失败

问题 :Spring Boot服务启动失败
解决方案

  • 检查端口是否被占用
  • 检查Spring Boot配置是否正确
  • 检查依赖是否完整

六、扩展与进阶建议

1. 替换为真实数据

  • 连接数据库:使用MySQL或其他数据库,存储真实的电商用户行为数据
  • 数据导入:使用数据导入工具将历史数据导入到数据库中
  • 数据预处理:对真实数据进行清洗和预处理,去除异常值和缺失值

2. 优化模型性能

  • 调整模型结构:尝试不同的网络层数和神经元数量
  • 尝试不同的激活函数:如LeakyReLU、ELU等
  • 调整优化器参数:如学习率、批次大小等
  • 使用早停法:当验证集损失不再下降时停止训练,防止过拟合

3. 添加更多特征

  • 用户特征:年龄、性别、地域等
  • 行为特征:浏览时长、购买时间分布、商品类别偏好等
  • 上下文特征:促销活动、节假日等
  • 社交特征:用户社交网络、推荐行为等

4. 增加模型监控

  • 记录预测结果:将预测结果和实际结果记录到数据库
  • 定期评估模型:定期计算模型在新数据上的性能指标
  • 模型版本管理:管理不同版本的模型,方便回滚和比较

5. 部署上线

  • 打包为Jar包 :使用mvn clean package命令打包
  • 部署到服务器:将Jar包部署到云服务器或容器平台
  • 配置负载均衡:根据流量配置负载均衡
  • 设置自动扩缩容:根据负载自动调整服务实例数量

七、总结

本项目是一个基于DL4J和Spring Boot的电商用户复购预测案例,通过这个项目,你可以:

  1. 了解深度学习基础概念:如神经网络、激活函数、损失函数等
  2. 掌握DL4J核心API:如MultiLayerNetwork、DataSet、NormalizerStandardize等
  3. 理解模型训练和预测流程:从数据准备到模型评估的完整流程
  4. 学习Spring Boot集成:如何将深度学习模型集成到Spring Boot应用中
  5. 实践电商场景应用:将深度学习技术应用到真实的电商业务场景中

深度学习是一个不断发展的领域,DL4J作为Java生态系统中的深度学习框架,为Java开发者提供了一种便捷的深度学习实现方式。希望本教程能帮助你入门DL4J,为你的项目开发提供新的思路和方法。

相关推荐
Victory_orsh13 小时前
AI雇佣人类,智能奴役肉体
后端
风指引着方向13 小时前
动态形状算子支持:CANN ops-nn 的灵活推理方案
人工智能·深度学习·神经网络
weixin_3954489113 小时前
cursor日志
人工智能·python·机器学习
踢足球092913 小时前
寒假打卡:2026-2-7
java·开发语言·javascript
凤希AI伴侣13 小时前
你觉得,AI能让你“一人成军”吗?我的工具流与真实体验
人工智能·凤希ai伴侣
闻哥13 小时前
Kafka高吞吐量核心揭秘:四大技术架构深度解析
java·jvm·面试·kafka·rabbitmq·springboot
23遇见13 小时前
从底层到落地:cann/ops-nn 算子库的技术演进与实践
人工智能
金牌归来发现妻女流落街头13 小时前
【Springboot基础开发】
java·spring boot·后端
DeanWinchester_mh13 小时前
DeepSeek新论文火了:不用卷算力,一个数学约束让大模型更聪明
人工智能·学习