MapReduce:分布式并行编程的高效基石‌

MapReduce:分布式并行编程的高效基石

在海量数据时代,如何在多个节点上高效地并行处理数据是分布式系统的核心挑战。Google 在 2004 年发布的 MapReduce 论文,提出了一种简洁而通用的编程模型——MapReduce。它将大数据计算拆分为“Map 阶段”和“Reduce 阶段”,允许开发者专注于业务逻辑,而由框架负责数据分发、容错和并行化执行。本文将通过代码示例与图解,详细说明 MapReduce 的原理与实现,帮助你快速掌握这一分布式并行编程范式。


目录

  1. MapReduce 概述
  2. MapReduce 编程模型

    • 2.1 Map 与 Reduce 函数定义
    • 2.2 Shuffle 和 Sort 过程
  3. 经典示例:WordCount

    • 3.1 环境准备
    • 3.2 Java 实现示例
    • 3.3 执行流程图解
  4. MapReduce 执行流程详解

    • 4.1 输入切分(Input Splits)
    • 4.2 Map Task 执行
    • 4.3 Shuffle 与 Sort
    • 4.4 Reduce Task 执行
    • 4.5 输出结果(Output)
  5. 高级概念与优化

    • 5.1 Combiner 的使用
    • 5.2 自定义分区(Partitioner)
    • 5.3 自定义排序(SortComparator)
    • 5.4 压缩与本地化
  6. MapReduce 框架演进与生态
  7. 总结

MapReduce 概述

MapReduce 作为一种编程模型及运行时框架,最初由 Google 在论文中提出,用于大规模分布式数据集的计算。其核心思想是将计算分为两个阶段:

  1. Map:从输入数据集中按行或按记录处理,将输入记录(key,value)映射为一组中间(keyʼ,valueʼ)对。
  2. Reduce:对具有相同 keyʼ 的中间结果进行汇总、聚合或其他处理,得到最终输出(keyʼ,result)。

通过这样的分工,MapReduce 框架可以在数百、数千台机器上并行执行 Map 和 Reduce 任务,实现海量数据的高效处理。同时,MapReduce 框架内置了容错机制(Task 重试、数据备份等)和自动化调度,使开发者无需关注底层细节。


MapReduce 编程模型

2.1 Map 与 Reduce 函数定义

  • Map 函数

    • 输入:一条记录(通常以 (key, value) 形式表示),如 (文件偏移量, 文本行)
    • 输出:零个或多个中间键值对 (keyʼ, valueʼ)
    • 作用:从数据中提取有意义的信息,生成可被聚合的中间结果。例如,将一句英文文本拆分成单词,并将每个单词输出为 (word, 1)
  • Reduce 函数

    • 输入:一个中间 keyʼ 以及属于该 keyʼ 的所有 valueʼ 列表
    • 输出:一个或多个最终键值对 (keyʼ, result)
    • 作用:对同一个 keyʼ 的所有中间结果进行合并处理,例如求和、计数、求最大/最小、拼接等操作。
以 WordCount(单词计数)为例,Map 函数将一行文本拆分单词并输出 (word, 1);Reduce 函数对同一个单词 word 的所有 1 值求和,得到 (word, totalCount)

2.2 Shuffle 和 Sort 过程

在 Map 阶段输出的所有 (keyʼ, valueʼ) 对,会经历一个 Shuffle & Sort(分布式洗牌与排序) 过程,主要包括以下步骤:

  1. Shuffle(分发)

    • 框架将 Map 任务输出按照 keyʼ 做哈希分区,确定要发给哪个 Reduce 节点。
    • 每个 Map 任务会将自己的中间结果分发给相应的 Reduce 节点,数据网络传输称为 “Shuffle”。
  2. Sort(排序)

    • 在每个 Reduce 节点上,收到来自多个 Map Task 的中间结果后,会根据 keyʼ 将这些 kv 对合并并进行排序(通常按字典序或自定义排序)。
    • 排序后的数据形成 (keyʼ, [valueʼ1, valueʼ2, ...]) 的形式,随后 Reduce 函数依次处理每个 keyʼ 及其对应的 value 列表。

图示示例:

+---------------------+       +---------------------+      +--------------+
|      Map Task 1     |       |      Map Task 2     | ...  |  Map Task M   |
|                     |       |                     |      |               |
| 输入: split1        |       | 输入: split2        |      | 输入: splitM   |
| 输出:               |       | 输出:               |      | 输出:         |
|   ("a",1),("b",1)...|       |   ("b",1),("c",1)...|      |   ("a",1),...  |
+---------+-----------+       +---------+-----------+      +-------+------+
          |                             |                          |
          |       Shuffle (按 key 分区)  |                          |
          +--------+        +-----------+--------+        +--------+
                   ▼        ▼                    ▼        ▼
               +-----------------------------------------------+
               |               Reduce Task 1                   |
               | 收到所有 key 哈希 % R == 0 的 ("a",1) ("a",1)…    |
               | Sort 后 -> ("a", [1,1,1...])                  |
               | Reduce("a", [1,1,1...]) -> ("a", total)       |
               +-----------------------------------------------+
                         ... Reduce Task 2 ... etc ...

以上过程保证同一个 key 的所有中间值都被调度到同一个 Reduce 任务,并在 Reduce 函数执行前已经完成了排序。


经典示例:WordCount

WordCount 是 MapReduce 中最经典的教程示例,用来统计文本中每个单词出现的次数。下面以 Apache Hadoop 的 Java API 为例,演示完整的实现。

3.1 环境准备

  1. JDK 1.8+
  2. Maven 构建工具
  3. Hadoop 3.x(可在本地伪分布式模式或者独立集群模式下运行)
  4. IDE(可选):IntelliJ IDEA、Eclipse 等

在项目的 pom.xml 中添加 Hadoop 相关依赖(示例版本以 Hadoop 3.3.4 为例):

<dependencies>
    <!-- Hadoop Common -->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>3.3.4</version>
    </dependency>
    <!-- Hadoop HDFS -->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>3.3.4</version>
    </dependency>
    <!-- Hadoop MapReduce Client Core -->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-core</artifactId>
        <version>3.3.4</version>
    </dependency>
</dependencies>

3.2 Java 实现示例

在 Hadoop MapReduce 中,需要实现以下几个核心类或接口:

  • Mapper 类:继承 Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
  • Reducer 类:继承 Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
  • Driver(主类):配置 Job、设置输入输出路径、提交运行

下面给出完整代码示例。

3.2.1 Mapper 类

package com.example.hadoop.wordcount;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * WordCount Mapper 类:
 * 输入:<LongWritable, Text> 对应 (偏移量, 文本行)
 * 输出:<Text, IntWritable> 对应 (单词, 1)
 */
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {

    // 定义常量,表示要输出的计数“1”
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
        // 将整行文本转换为 String,再按空白符拆分单词
        String line = value.toString();
        String[] tokens = line.split("\\s+");
        for (String token : tokens) {
            if (token.length() > 0) {
                word.set(token);
                // 输出 (单词, 1)
                context.write(word, one);
            }
        }
    }
}

3.2.2 Reducer 类

package com.example.hadoop.wordcount;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * WordCount Reducer 类:
 * 输入:<Text, Iterable<IntWritable>> 对应 (单词, [1,1,1,...])
 * 输出:<Text, IntWritable> 对应 (单词, 总次数)
 */
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

    private IntWritable result = new IntWritable();

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
        int sum = 0;
        // 对同一个 key(单词)的所有 value 求和
        for (IntWritable val : values) {
            sum += val.get();
        }
        result.set(sum);
        // 输出 (单词, 总次数)
        context.write(key, result);
    }
}

3.2.3 Driver(主类)

package com.example.hadoop.wordcount;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

/**
 * WordCount 主类:配置 Job 并提交运行
 */
public class WordCountDriver {

    public static void main(String[] args) throws Exception {
        // args[0] = 输入路径, args[1] = 输出路径
        if (args.length != 2) {
            System.err.println("Usage: WordCountDriver <input path> <output path>");
            System.exit(-1);
        }

        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "Word Count Example");
        job.setJarByClass(WordCountDriver.class);

        // 设置 Mapper 类与输出类型
        job.setMapperClass(WordCountMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        // 设置 Reducer 类与输出类型
        job.setReducerClass(WordCountReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        // 指定输入格式与路径
        job.setInputFormatClass(TextInputFormat.class);
        TextInputFormat.addInputPath(job, new Path(args[0]));

        // 指定输出格式与路径
        job.setOutputFormatClass(TextOutputFormat.class);
        TextOutputFormat.setOutputPath(job, new Path(args[1]));

        // Submit job and wait for completion
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

3.2.4 运行部署

  1. 编译打包
    在项目根目录执行:

    mvn clean package -DskipTests

    会生成一个包含全部依赖的可运行 Jar(如果配置了 Maven Shade 或 Assembly 插件)。

  2. 将 Jar 上传至 Hadoop 集群节点,并将输入文本放到 HDFS:

    hdfs dfs -mkdir -p /user/hadoop/wordcount/input
    hdfs dfs -put local_input.txt /user/hadoop/wordcount/input/
  3. 执行 MapReduce 作业

    hadoop jar target/wordcount-1.0.jar \
      com.example.hadoop.wordcount.WordCountDriver \
      /user/hadoop/wordcount/input /user/hadoop/wordcount/output
  4. 查看结果

    hdfs dfs -ls /user/hadoop/wordcount/output
    hdfs dfs -cat /user/hadoop/wordcount/output/part-*

3.3 执行流程图解

下面通过图解,展示 WordCount 作业从输入到输出的全过程(假设有 2 个 Map Task、2 个 Reduce Task)。

        ┌────────────────────────────────────────────┐
        │             输入文件(HDFS)              │
        │  /user/hadoop/wordcount/input/local.txt    │
        └────────────────────────────────────────────┘
                         │
                         │ 切分为两个 InputSplit
                         ▼
        ┌────────────────────┐      ┌────────────────────┐
        │  Split 1 (Block1)  │      │  Split 2 (Block2)  │
        │ (lines 1~500MB)    │      │ (lines 501~1000MB) │
        └────────────────────┘      └────────────────────┘
                 │                          │
                 │                          │
       Fork Map Task 1              Fork Map Task 2
                 │                          │
                 ▼                          ▼
┌────────────────────────────────────────────────────────────────┐
│                      Map Task 1 (节点 A)                       │
│ Inputs: Split 1 (一行行文本)                                  │
│ for each line:                                                 │
│   split by whitespace → emit (word, 1)                          │
│ Outputs:                                                     ┌──────────┐
│   ("hello",1),("world",1),("hello",1),…                       │ Shuffle  │
│                                                               └──────────┘
└────────────────────────────────────────────────────────────────┘
                 │                          │
                 │                          │
┌────────────────────────────────────────────────────────────────┐
│                      Map Task 2 (节点 B)                       │
│ Inputs: Split 2                                               │
│ for each line:                                                │
│   split by whitespace → emit (word, 1)                          │
│ Outputs: ("world",1),("foo",1),("bar",1),…                     │
│                                                               │
└────────────────────────────────────────────────────────────────┘
                 │                          │
                 │        中间结果分发(Shuffle)          │
          ┌──────┴──────┐               ┌──────┴──────┐
          │  Reduce 1   │               │  Reduce 2   │
          │  Key Hash %2=0 │            │  Key Hash %2=1 │
          └──────┬──────┘               └──────┬──────┘
                 │                                 │
   收到 Map1: ("hello",1),("hello",1), …        收到 Map1: ("world",1), …
   收到 Map2: ("foo",1),("foo",1), …            收到 Map2: ("bar",1),("world",1),…
                 │                                 │
   Sort 排序后:("foo",[1,1,…])                  Sort 排序后:("bar",[1]),("world",[1,1,…])
                 │                                 │
    Reduce 处理:                                Reduce 处理:
    sum([1,1,…]) → ("foo", totalFoo)             sum([1]) → ("bar",1)
    emit ("foo", nFoo)                           emit ("bar",1)
    emit ("hello", nHello)                       sum([1,1,…]) → ("world", nWorld)
                                                 emit ("world", nWorld)
                 │                                 │
          ┌──────┴──────┐               ┌──────┴──────┐
          │ 输出 Part-00000 │             │ 输出 Part-00001 │
          └────────────────┘             └────────────────┘
                 │                                 │
                 │                                 │
        ┌────────────────────────────────────────────────┐
        │            最终输出保存在 HDFS               │
        │ /user/hadoop/wordcount/output/part-*         │
        └────────────────────────────────────────────────┘
  • InputSplit:HDFS 将大文件切分为若干块(Block),对应一个 Map Task。
  • Map:对每行文本生成 (word,1) 中间对。
  • Shuffle:根据单词的哈希值 % 索引 分发给不同 Reduce。
  • Sort:在每个 Reduce 节点,对收到的中间对按 key 排序、归并。
  • Reduce:对同一个单词的所有 1 值求和,输出最终结果。

MapReduce 执行流程详解

下面更细致地剖析 MapReduce 作业在 Hadoop 或类似框架下的执行流程。

4.1 输入切分(Input Splits)

  1. 切分逻辑

    • Hadoop 会将输入文件按 HDFS Block 大小(默认 128MB)切分,形成若干个 InputSplit。每个 InputSplit 通常对应一个 Map Task。
    • 如果一个文件非常大,就会产生很多 Split,从而并行度更高。
    • 可以通过配置 mapreduce.input.fileinputformat.split.maxsizemapreduce.input.fileinputformat.split.minsize 等参数控制切分策略。
  2. 数据本地化

    • Map Task 会优先发给持有对应 Block 副本的节点运行,以提高数据本地化率,减少网络传输。

4.2 Map Task 执行

  1. 读取 Split

    • 输入格式(InputFormat)决定如何读取 Split。例如 TextInputFormat 会按行读取,Key 为文件偏移量(LongWritable),Value 为文本行(Text)。
    • 开发者可以自定义 InputFormat,实现对不同数据源(CSV、JSON、SequenceFile)的读取解析。
  2. Map 函数逻辑

    • 每个 Map Task 都会对该 Split 中的每一条记录调用用户实现的 map(KEYIN, VALUEIN, Context) 方法。
    • Map 函数可输出零个、一个或多个中间 (KEYOUT, VALUEOUT) 对。
  3. Combiner(可选)

    • Combiner 类似于“本地 Reduce”,可以在 Map 端先对中间结果做一次局部合并,减少要传输到 Reduce 的数据量。
    • Combiner 的工作方式是:Map 输出先落盘到本地文件,然后 Combiner 从本地读取进行合并,最后再写入到 Shuffle 缓存。
    • 对于可交换、可结合的运算(如求和、计数),使用 Combiner 可以显著减少网络带宽消耗。

4.3 Shuffle 与 Sort

  1. Partitioner(分区)

    • 默认使用 HashPartitioner,即 hash(key) % reduceTasks,决定中间 key 属于哪个 Reduce Task。
    • 可以通过继承 Partitioner 来自定义分区策略,例如按某个字段范围分区,实现更均衡的负载。
  2. Shuffle 数据传输

    • Map Task 执行完成后,会将中间结果写入本地磁盘,并通过多个内存缓冲区暂存。
    • 当内存缓冲区达到一定阈值(默认 80%),Map Task 会将缓冲区中的数据写到本地文件并触发一次“Map 输出文件合并”。
    • Reduce Task 启动后,会向各个 Map Task 发起 HTTP 请求,拉取自己所需分区的中间文件(segments),并写入本地临时目录。
  3. 排序(Sort)

    • Reduce Task 拉取完所有 Map Task 的分区后,会在本地对这些中间文件进行合并排序,按 key 升序排列,产出 (key, [value1, value2, ...]) 的格式。
    • 这个排序过程分两阶段:若数据量过大,先将每个 Map 传输来的分区输出按key本地排序并写入磁盘;然后对所有文件再做多路归并排序。

4.4 Reduce Task 执行

  1. Reduce 函数调用

    • 在每个 Reducer 中,排序完成后会对每个 key 及对应的 value 列表调用一次用户实现的 reduce(KEYIN, Iterable<VALUEIN>, Context) 方法。
    • 开发者在 Reduce 中对 value 列表做聚合处理(如求和、取平均、拼接字符串、过滤等)。
    • Reduce 完成后,通过 context.write(key, outputValue) 输出到最终结果文件。
  2. 输出结果写入 HDFS

    • 每个 Reduce Task 会将输出结果写到 HDFS 上的一个文件,文件名通常为 part-r-00000part-r-00001 等。
    • 如果 Reduce 数量为 N,则最终输出会生成 N 个 part 文件。

4.5 输出结果(Output)

  • MapReduce 作业执行完成后,最终输出目录下会包含若干个 part 文件(和一个 _SUCCESS 成功标志文件)。
  • 用户可以直接在 HDFS 上查看,也可以将结果下载到本地进一步分析。
  • 如果需要将结果进一步加工,可以通过后续的 MapReduce Job、Hive、Spark 等进行二次处理。

高级概念与优化

在实际生产环境中,单纯的 Map 和 Reduce 通常无法满足更复杂场景。以下介绍几个常见的高级概念与优化技巧。

5.1 Combiner 的使用

  • 作用:在 Map Task 端对中间结果做局部聚合,减少网络传输开销。
  • 使用场景:适用于满足“交换律、结合律”运算的场景,如计数求和、求最大/最小。
  • 注意事项:Combiner 只是一个“建议”,框架不保证一定会调用;对 Reducer 函数需要足够“安全”(去重或关联的逻辑,Combiner 可能导致结果不正确)。
job.setCombinerClass(WordCountReducer.class);
// Combiner 通常直接使用与 Reducer 相同的逻辑

图解示例(WordCount 中):

Map Output: ("foo",1),("foo",1),("bar",1),("foo",1)... 
   ↓ (Combiner)
Local Combine: ("foo",3),("bar",1) 
   ↓ 向各个 Reducer Shuffle

5.2 自定义分区(Partitioner)

  • 默认分区:HashPartitioner 按 key 的 hash 值对 Reduce 数量取模。
  • 自定义分区:继承 Partitioner<KEY, VALUE> 并实现 getPartition(KEY key, VALUE value, int numPartitions) 方法。
  • 应用场景

    • 数据倾斜:通过自定义逻辑,将热点 key 分布到更多 Reducer 上。
    • 范围分区:按数值区间或时间窗口分区。

示例:按单词首字母范围分区,0-9 开头发给 Reducer0,A-M 发给 Reducer1,N-Z 发给 Reducer2。

public class CustomPartitioner extends Partitioner<Text, IntWritable> {
    @Override
    public int getPartition(Text key, IntWritable value, int numPartitions) {
        char first = Character.toLowerCase(key.toString().charAt(0));
        if (first >= 'a' && first <= 'm') {
            return 0 % numPartitions;
        } else if (first >= 'n' && first <= 'z') {
            return 1 % numPartitions;
        } else {
            return 2 % numPartitions;
        }
    }
}
// 在 Driver 中引用
job.setPartitionerClass(CustomPartitioner.class);
job.setNumReduceTasks(3);

5.3 自定义排序(SortComparator)与 GroupingComparator

  • SortComparator(排序比较器)

    • 用来覆盖默认的 key 排序逻辑(字典序),可自定义升序、降序或复合排序规则。
    • 继承 WritableComparator 并实现 compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2),或者简单地实现 RawComparator<KEY>
  • GroupingComparator(分组比较器)

    • 用来控制将哪些 key 视为“同一组”传入某次 Reduce 调用。
    • 例如,key 为 (userid, pageurl),我们想按照 userid 分组,则自定义分组比较器只比较 userid 部分。

示例:按 year-month 进行Reduce 分组,而排序则按 year-month-day 进行。

// 假设 Key = Text 格式为 "YYYY-MM-DD"
// 自定义分组比较器,只比较 "YYYY-MM"
public class YearMonthGroupingComparator extends WritableComparator {
    public YearMonthGroupingComparator() {
        super(Text.class, true);
    }
    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        String s1 = a.toString().substring(0, 7); // "YYYY-MM"
        String s2 = b.toString().substring(0, 7);
        return s1.compareTo(s2);
    }
}
// 在 Driver 中引用
job.setGroupingComparatorClass(YearMonthGroupingComparator.class);

5.4 压缩与本地化

  • Map 输出压缩(Intermediate Compression)

    • 使用 mapreduce.map.output.compress=truemapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec 等配置,可压缩 Map 任务输出,降低 Shuffle 传输带宽。
  • Reduce 输出压缩

    • 设置 mapreduce.output.fileoutputformat.compress=true 等,可将最终输出结果压缩存储。
  • 数据本地化

    • 通过提高数据本地化率(mapreduce.job.reduce.slowstart.completedmaps 等参数),可以减少 Reduce 拉取远程数据的比例,提高整体性能。

MapReduce 框架演进与生态

虽然 MapReduce 曾是大数据处理的主流框架,但随着技术发展,Apache Spark、Flink 等内存计算引擎已经广泛应用。不过,MapReduce 依旧具备以下优势:

  • 稳定成熟:Hadoop MapReduce 经历多年生产环境考验,生态完善。
  • 磁盘容错:依赖 HDFS 存储与 Checkpoint,任务可在任意节点失败后恢复。
  • 编程模型简单:只需实现 Map/Reduce 函数,无需关注底层并行调度。

常见衍生生态:

  1. Hive:基于 MapReduce(也可切换 Spark、Tez)实现 SQL-on-Hadoop。
  2. Pig:提供数据流式脚本语言,底层编译为一系列 MapReduce 作业。
  3. HBase BulkLoad:借助 MapReduce 批量导入 HBase。
  4. Sqoop:将关系型数据库数据导入 Hadoop,支持 MapReduce 并行导入。

总结

  • MapReduce 编程模型 以简洁的 Map/Reduce 接口,使开发者专注于“如何处理数据”,而将“并行化、容错、网络分发”等复杂工作交由框架负责。
  • 核心流程 包括:输入切分 → Map 任务 → Shuffle & Sort → Reduce 任务 → 输出结果。
  • 经典示例 WordCount 展示了如何在分布式集群上统计单词频次,从切分、Map、Shuffle、Reduce 到最终输出,整个过程实现了高效并行。
  • 优化手段 如 Combiner、自定义 Partitioner、Sorting/GroupingComparator、压缩等,可进一步提升 MapReduce 作业在大规模数据处理时的性能和稳定性。

通过本文的代码示例与图解,相信你已经对 MapReduce 模型与 Hadoop 实现有了更直观的理解。对于学习分布式并行编程的入门来说,掌握 MapReduce 是很好的切入点。当你的数据处理需求更加实时化、流式化时,可以进一步学习 Spark、Flink 等内存计算框架,它们在模型设计上借鉴了 MapReduce 的思想,但更加灵活高效。

最后修改于:2025年06月02日 21:00

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日