Global View的概念和实现|OneFlow学习笔记

96f3701cc3efe68420b303e80a7c0354.png

撰文|月踏

更新|赵露阳

在OneFlow中,Global View也被称作一致性视角,用来把一个物理集群抽象成一个逻辑设备,并使用Placement和SBP来实现这种抽象。本文从基本概念、数据结构、接口实现等方面对其进行学习和总结。

1

Placement

1.1 使用示例

Placement用来描述设备信息,包括设备类型、设备分布信息,先看一个具体的使用示例,然后根据这个示例来做分析:

import oneflow as flow
x = flow.placement(type="cuda", ranks=[[0, 1, 2, 3], [4, 5, 6, 7]])

type(x)的输出为:

<class 'oneflow._oneflow_internal.placement'>
print(x)的输出为:
oneflow.placement(type="cuda", ranks=[[0, 1, 2, 3], [4, 5, 6, 7]])
可见Placement有下面两个属性:
  1. type:表示设备类型,目前只支持CPU和CUDA

  2. ranks:一个Python list,用于表示device的排布信息,ranks可以是一维至多维的,其shape表示了设备的排布信息(hierarchy)。上述ranks表示Tensor存放在集群中的2个节点中,其中节点1中使用设备0~3,节点2中使用设备4~7。

1.2 追踪代码

先看Python端的接口,在python/oneflow/__init__.py+27可以看到下面语句:

placement = oneflow._oneflow_internal.placement
 
 

可见Placement是在前文《OneFlow学习笔记:python到C++调用过程分析》

讲的一个pybind定义的_oneflow_internal这个module的子module,在oneflow/api/python/symbol/placement_symbol.cpp+226可以找到下面的定义:
ONEFLOW_API_PYBIND11_MODULE("", m) {
py::class_<Symbol<ParallelDesc>, std::shared_ptr<Symbol<ParallelDesc>>>(m, "placement",
py::dynamic_attr())
.def(...)
.def(py::init([](const std::string& type, const py::object& ranks) {
return
PlacementSymbolExportUtil::CreateParallelDescSymbol(type, ranks).GetOrThrow();
}),
py::arg("type"), py::arg("ranks"))
 
 

通过上面的多个def接口可以看到,通过调用PlacementSymbolExportUtil::CreateParallelDescSymbol()来构造Placement对象,这个函数是个重载函数,定义在同一个文件中,多个重载版本只是参数有区别,其中的创建Placement的逻辑基本一致,下面列一个重载版本作为示例:

// create Symbol<ParallelDesc> object through given device_type and ranks parameters
static Maybe<Symbol<ParallelDesc>> CreateParallelDescSymbol(const std::string& type,
                                                            const py::object& ranks) {
  auto* obj = reinterpret_cast<PyArrayObject*>(PyArray_FromAny(
      ranks.ptr(), nullptr, 0, 0, NPY_ARRAY_DEFAULT | NPY_ARRAY_ENSURECOPY, nullptr));
  if (!obj) { return Error::RuntimeError() << "placement ranks must be int64 array."; }


  const auto& shape = JUST(GetRanksShape(obj));
  const auto& formated_machine_device_ids = JUST(ParseAndFormatRanks(obj));
  return SymbolOf(*JUST(CreateParallelDesc(type, *formated_machine_device_ids, shape)));
}

由这个函数的返回值可见,Placement在C++中对应的数据结构是ParallelDesc,这个数据结构后面再说,现在先继续看创建逻辑,这里继续调用了CreateParallelDesc函数,同样定义在PlacementSymbolExportUtil这个类中:

static Maybe<ParallelDesc> CreateParallelDesc(
    const std::string& type, const std::vector<std::string>& formated_machine_device_ids,
    const std::shared_ptr<Shape>& hierarchy_shape) {
  JUST(CheckDeviceTag(type));
  auto parallel_conf = JUST(MakeParallelConf(type, formated_machine_device_ids, hierarchy_shape));
  std::shared_ptr<ParallelDesc> parallel_desc;
  JUST(PhysicalRun([&parallel_desc, &parallel_conf](InstructionsBuilder* builder) -> Maybe<void> {
      parallel_desc = JUST(builder->GetParallelDescSymbol(parallel_conf));
      return Maybe<void>::Ok();
  }));
  
  return parallel_desc;
}

这里最重要的是调用MakeParallelConf这个函数,位于oneflow/core/framework/parallel_conf_util.cpp+38,它根据传入的device type、machine device ids、hierarchy shape信息创建了一个cfg::ParallelConf类型的对象parallel_conf。

这里需要注意的是,hierarchy shape表示设备的排放层次序列,即上面通过const auto& shape = JUST(GetRanksShape(obj));得到的由ranks参数所表示的list shape。创建完parallel_conf之后,通过后面的GetParallelDescSymbol接口来得到需要返回的ParallelDesc类型对象,下面是MakeParallelConf的主要实现:

Maybe<cfg::ParallelConf> MakeParallelConf(const std::string& device_tag,
                                          const std::vector<std::string>& machine_device_ids,
                                          const std::shared_ptr<Shape>& hierarchy) {
  std::shared_ptr<cfg::ParallelConf> parallel_conf = std::make_shared<cfg::ParallelConf>();
  parallel_conf->set_device_tag(device_tag);
  for (const std::string& machine_device_id : machine_device_ids) {
    size_t pos = machine_device_id.find(':');
    CHECK_NE_OR_RETURN(pos, std::string::npos) << "device_name: " << machine_device_id;
    std::string machine_id = machine_device_id.substr(0, pos);
    CHECK_OR_RETURN(
        (IsStrInt(machine_id) || (machine_id[0] == '@' && IsStrInt(machine_id.substr(1)))))
        << " machine_id: " << machine_id;
    std::string device_id = machine_device_id.substr(pos + 1);
    size_t minus_pos = device_id.rfind('-');
    if (minus_pos == std::string::npos) {
      CHECK_OR_RETURN(IsStrInt(device_id));
    } else {
      std::string min_id = device_id.substr(0, minus_pos);
      CHECK_OR_RETURN(IsStrInt(min_id));
      std::string max_id = device_id.substr(minus_pos + 1);
      CHECK_OR_RETURN(IsStrInt(max_id));
    }
    parallel_conf->add_device_name(machine_device_id);
    if (hierarchy) {
      ShapeProto proto;
      hierarchy->ToProto(&proto);
      parallel_conf->mutable_hierarchy()->CopyFrom(cfg::ShapeProto(proto));
    }
  }
  return parallel_conf;
}

再继续看下GetParallelDescSymbol是怎么根据cfg::ParallelConf的对象得到ParallelDesc类型对象的,GetParallelDescSymbol定义在oneflow/core/framework/instructions_builder.cpp+230:

Maybe<ParallelDesc> InstructionsBuilder::GetParallelDescSymbol(
    const std::shared_ptr<cfg::ParallelConf>& parallel_conf) {
  int64_t symbol_id = JUST(FindOrCreateSymbolId(*parallel_conf));
  return Global<symbol::Storage<ParallelDesc>>::Get()->MaybeGetPtr(symbol_id);
}
大概过程就是在一个全局表里面去查有没有cfg::ParallelConf对应的已经创建好的ParallelDesc的对象,有的话直接返回,没有的话就创建出来放到全局表中去,至此就得到了前面展示的pybind接口中需要的ParallelDesc对象。

下面继续看下相关的数据结构,主要是cfg::ParallelConf和ParallelDesc,它们都和下面这个proto文件相关:

  • oneflow/core/job/placement.proto

这个proto文件是所有placement相关数据结构的源头,根据它会先自动生成下面三个文件:

  • build/oneflow/core/job/placement.pb.h

  • build/oneflow/core/job/placement.pb.cc

  • build/of_cfg_proto_python/oneflow/core/job/placement_pb2.py

前两者的接口主要是为了对placement数据做序列化,但是这些接口不适合对接python,所以使用tools/cfg中的工具对第三个文件做了处理,生成了下面三个方便给python端提供接口的文件:

  • build/oneflow/core/job/placement.cfg.h

  • build/oneflow/core/job/placement.cfg.cpp

  • build/oneflow/core/job/placement.cfg.pybind.cpp

cfg::ParallelConf这个数据结构就定义在build/oneflow/core/job/placement.cfg.h这个自动生成的文件中,再看ParallelDesc,它其实可以看作是cfg::ParallelConf的一层wrapper,主要是用在c++代码中来表示placement的数据结构,位于oneflow/core/job/parallel_desc.h+46,我们只需要关注这个数据结构就行:

class ParallelDesc final {
  ...
  ...
  Optional<int64_t> symbol_id_;
  DeviceType device_type_;
  ParallelConf parallel_conf_;
  std::shared_ptr<Shape> hierarchy_;
  std::vector<int64_t> sorted_machine_ids_;
  std::shared_ptr<HashMap<int64_t, std::shared_ptr<std::vector<int64_t>>>>
      machine_id2sorted_dev_phy_ids_;
  int64_t parallel_num_;
  int64_t device_num_of_each_machine_;
  std::vector<int64_t> parallel_id2machine_id_;
  std::vector<int64_t> parallel_id2device_id_;
  HashMap<int64_t, HashMap<int64_t, int64_t>> machine_id2device_id2parallel_id_;
  // TODO(lixinqi): merge cfg_parallel_conf_ and parallel_conf_ after cfg::ParallelConf taken as the
  // constructor argument
  std::shared_ptr<cfg::ParallelConf> cfg_parallel_conf_;
  // cached result of ContainingMachineId(GlobalProcessCtx::Rank()) for performace optimization.
  bool containing_current_rank_;
};

这里面的数据结构看起来很复杂,我也不完全明白所有成员的含义,但归根结底这里数据成员的值还都是根据cfg::ParallelConf中的内容来的,在前面调用GetParallelDescSymbol时,如果全局表中没有找到,就会根据cfg::ParallelConf类型对象创建一个ParallelConf类型对象,再根据这个ParallelConf类型对象创建一个ParallelDesc类型对象,在ParallelDesc的构造函数中会调用类内的MaybeInit函数,位于oneflow/core/job/parallel_desc.cpp+112,这里面会完成ParallelDesc数据成员的赋值:

Maybe<void> ParallelDesc::MaybeInit(const ParallelConf& user_conf) {
  parallel_conf_ = user_conf;
  device_type_ = DeviceType::kInvalidDevice;
  const std::string& device_tag = parallel_conf_.device_tag();
  DeviceType device_type = JUST(DeviceType4DeviceTag(device_tag));
  CHECK_OR_RETURN(device_type_ == DeviceType::kInvalidDevice || device_type_ == device_type);
  device_type_ = device_type;
  machine_id2sorted_dev_phy_ids_ =
      std::make_shared<HashMap<int64_t, std::shared_ptr<std::vector<int64_t>>>>();
  for (const std::string& device_name : parallel_conf_.device_name()) {
    if (device_name[0] == '@') {
      JUST(SetMachineIdAndDeviceIdsByParsingDeviceName(device_name.substr(1), 1));
    } else {
      JUST(SetMachineIdAndDeviceIdsByParsingDeviceName(device_name,
                                                       GlobalProcessCtx::NumOfProcessPerNode()));
    }
  }
  containing_current_rank_ = machine_id2sorted_dev_phy_ids_->count(GlobalProcessCtx::Rank()) > 0;
  ClearUp();
  JUST(SanityCheck());
  return Maybe<void>::Ok();
}

以上就是在python端使用placement时从上到下的大概过程和placement相关的数据结构。

2

SBP

2.1 基本概念

SBP是OneFlow发明的概念,在OneFlow的官方文档和论文中都有详细的说明(具体链接都在文末Reference中列出),这里只做简单介绍,它是下面三个单词的缩写:

  • Split:表示把数据按照指定的维度进行切分,被切分出的数据块会被分发到前面Placement指定的各个物理设备中去

  • Broadcast:表示把整份数据广播到前面Placement指定的各个物理设备中去

  • Partial:表示前面Placement指定的各个物理设备中所存的数据不是最终的运算结果,需要对各个物理设备上的数据进行Elementwise的add/min/max等操作,才能得到最终的结果

SBP描述了一致性视角下的数据与物理设备上的数据的映射关系,计算的时候,数据会根据自己的SBP属性被分发到各个物理设备进行计算,下面贴一张OneFlow官方文档的截图来直观的展示SBP的三种情况:

8ef1f4c9e433838a5e6bfac005119efa.png

图1

2.2 使用示例

在Python环境做下面这个简单的示例:

import oneflow as flow
s=flow.sbp.split(1)
b=flow.sbp.broadcast
p=flow.sbp.partial_sum

type(s)、type(b)、type(p)的输出如下:

<class 'oneflow._oneflow_internal.sbp.sbp'>
<class 'oneflow._oneflow_internal.sbp.sbp'>
<class 'oneflow._oneflow_internal.sbp.sbp'>

print(s)、print(b)、print(p)的输出如下:

oneflow.sbp.split(axis=1)
oneflow.sbp.broadcast
oneflow.sbp.partial_sum

2.3 追踪代码

先找入口,在python/oneflow/__init__.py+196:

from . import sbp
这用到了同目录下的这个module文件:python/oneflow/sbp.py,内容如下:
import oneflow
from oneflow.framework.distribute import split_sbp as split
import oneflow._oneflow_internal


sbp = oneflow._oneflow_internal.sbp.sbp
broadcast = oneflow._oneflow_internal.sbp.broadcast()
partial_sum = oneflow._oneflow_internal.sbp.partial_sum()


# 其中split_sbp的定义如下
def split_sbp(axis: int) -> oneflow._oneflow_internal.sbp.sbp:
    assert type(axis) is int
    return oneflow._oneflow_internal.sbp.split(axis)

可见split、broadcast和partial_sum都是定义在pybind定义的_oneflow_internal这个module的子module sbp的内部,在oneflow/api/python/symbol/sbp_symbol.cpp+85可以找到下面定义:

ONEFLOW_API_PYBIND11_MODULE("sbp", m) {
  m.attr("max_split_axis") = kMaxSplitAxis;
  py::class_<Symbol<SbpParallel>, std::shared_ptr<Symbol<SbpParallel>>>(m, "sbp",
                                                                        py::dynamic_attr())
      .def("__str__", &api::SbpToString)
  ...
  ...
  m.def("split", GetSplitSbpParallel, py::arg("axis"));
  m.def("broadcast", &GetBroadcastSbpParallel);
  m.def("partial_sum", &GetPartialSumSbpParallel);
}

可以看到sbp对接python接口时用的是cfg::SbpParallel这个数据结构,这里它和placement中的cfg::ParallelConf一样,同样下面这个proto文件自动生成出来:

  • oneflow/core/job/sbp_parallel.proto

编译的时候protoc会先根据这个proto文件生成下面三个文件:

  • build/oneflow/core/job/sbp_parallel.pb.h

  • build/oneflow/core/job/sbp_parallel.pb.cc

  • build/of_cfg_proto_python/oneflow/core/job/sbp_parallel_pb2.py

其中前两个文件提供接口用于对SBP数据做序列化,接口都属于oneflow namespace,第三个文件结合tools/cfg中的工具用于生成下面三个文件给Python端来用,文件中的接口属于cfg namespace:

  • build/oneflow/core/job/sbp_parallel.cfg.h

  • build/oneflow/core/job/sbp_parallel.cfg.cpp

  • build/oneflow/core/job/sbp_parallel.cfg.pybind.cpp

OneFlow的内部c++代码中用的是cfg::NdSbp这个数据结构,它其实可以看作是vector<cfg::SbpParallel>,这些数据结构之间的关系,直接看proto文件最为直接:

message SplitParallel { required int64 axis = 1; }
message BroadcastParallel { }
message PartialSumParallel { }
message SbpParallel {
  oneof parallel_type {
    SplitParallel split_parallel = 1;
    BroadcastParallel broadcast_parallel = 2;
    PartialSumParallel partial_sum_parallel = 3;
  }
}
message SbpSignature { map<string, SbpParallel> bn_in_op2sbp_parallel = 1; }
message NdSbp { repeated SbpParallel sbp_parallel = 1; }
message NdSbpSignature { map<string, NdSbp> bn_in_op2nd_sbp = 1; }
message SbpSignatureList { repeated SbpSignature sbp_signature = 1; }

‍

继续看前面定义SBP的Python接口时所调用的GetSplitSbpParallel、GetBroadcastSbpParallel、GetPartialSumSbpParallel这三个C++函数,位于oneflow/api/python/symbol/sbp_symbol.cpp+41:

Maybe<Symbol<SbpParallel>> GetSplitSbpParallel(int axis) {
  CHECK_LT_OR_RETURN(axis, kMaxSplitAxis);
  static std::vector<Symbol<SbpParallel>> split_sbp_sym_list =
      *JUST(MakeSplitSbpParallelList(kMaxSplitAxis));
  return split_sbp_sym_list.at(axis);
}


Maybe<Symbol<SbpParallel>> GetBroadcastSbpParallel() {
  static Symbol<SbpParallel> broadcast_sbp = JUST(MakeBroadcastSbpParallel());
  return broadcast_sbp;
}


Maybe<Symbol<SbpParallel>> GetPartialSumSbpParallel() {
  static Symbol<SbpParallel> partial_sum_sbp = JUST(MakePartialSumSbpParallel());
  return partial_sum_sbp;
}

它们各自又分别调用了MakeSplitSbpParallel、MakeBroadcastSbpParallel、MakePartialSumSbpParallel这三个函数,位于oneflow/core/job/sbp_parallel.cpp+68:

Maybe<Symbol<SbpParallel>> MakeSplitSbpParallel(int axis) {
  CHECK_LT_OR_RETURN(axis, kMaxSplitAxis);
  SbpParallel split_sbp_parallel;
  split_sbp_parallel.mutable_split_parallel()->set_axis(axis);
  return SymbolOf(split_sbp_parallel);
}


Maybe<Symbol<SbpParallel>> MakeBroadcastSbpParallel() {
  SbpParallel broadcast_sbp;
  broadcast_sbp.mutable_broadcast_parallel();
  return SymbolOf(broadcast_sbp);
}


Maybe<Symbol<SbpParallel>> MakePartialSumSbpParallel() {
  SbpParallel partial_sum_sbp;
  partial_sum_sbp.mutable_partial_sum_parallel();
  return SymbolOf(partial_sum_sbp);
}

SymbolOf背后用到的是Symbol这个OneFlow的基本组件,它的实现就不在这里展开了,总体来讲,它是把创建的对象维护到下面这个全局的SymbolMap中:

std::unordered_map<HashEqTraitPtr<const T>, std::shared_ptr<const T>>;
这样以后再用到的话,如果已经存在就不需要重新创建了,直接返回就好。

3

Global Tensor

Tensor的基本概念就不用说了,相信没有人不知道,在OneFlow的设计中,Global Tensor就是为了能够满足Global View所需抽象的一种Tensor,里面需要有前面讲的Placement和SBP相关的属性,下面把OneFlow所有Tensor一并总结列出:

0e2981ddbc172c3b454e2a8ec246ebc7.png

图2

OneFlow的Tensor设计采用了bridge design pattern,把接口和实现做了分离,在Global View的情况下,用到的是上图中的ConsistentTensor(0.7版统称GlobalTensor),可以看到它持有一个指向ConsistentTensorImpl的指针,真正的实现就在ConsistentTensorImpl这个类中,下面是TensorImpl系列类的hierarchy图示:

146b69ba7f3895f9cd87d166e1ef606d.png

 图3

先看这个图里的基类部分,橙色部分是它包含的数据成员,总体来讲这个基类维护了一些用于反向求导的信息。

再看EagerConsistentTensorImpl,前面讲过,Global View实际上是一个逻辑视角,对应的global tensor实际上也是个逻辑tensor,那么它实际的数据存在于集群的每台机器的每张卡对应的tensor中,即图2中的MirroredTensor中,EagerConsistentTensorImpl持有指向MirroredTensor的指针,MirroredTensor持有指向MirroredTensorImpl的指针,MirroredTensorImpl的子类EagerMirroredTensorImpl中则持有指向TensorStorage的指针,tensor中的数据最终是存在于TensorStorage对象中,它定义在oneflow/core/eager/eager_blob_object.h+32,下面是主要的数据成员:

class TensorStorage {
  ...
  size_t blob_bytes_;
  std::unique_ptr<char, std::function<void(char*)>> blob_dptr_;
  std::unique_ptr<MemoryAllocator> non_pod_allocator_;
  Optional<Symbol<Stream>> producer_stream_;
  Optional<Symbol<Stream>> last_used_stream_;
  std::vector<std::function<void()>> storage_delete_hooks_;
};

继续看ConsistentTensorImpl,它持有一个指向ConsistentTensorMeta的指针,TensorMeta这个系列类维护了Tensor的一些元信息,如shape、data_type、device等,如果要是ConsistentTensor的话,还会持有placement和SBP的信息,下面是TensorMeta系列类的hierarchy图示:

795bfdd84485f8ef8a6590e461735dad.png

图4

可以看到在ConsistentTensorMeta中,维护了Placement和SBP的信息。

本文大概梳理了一下Global View的基本概念和部分具体实现,主要的参考资料是OneFlow的官方代码、官方文档和论文,以下是具体链接:

1.https://github.com/Oneflow-Inc/oneflow

2.https://arxiv.org/abs/2110.15032

3.https://docs.oneflow.org/master/parallelism/02_sbp.html

4.https://docs.oneflow.org/master/parallelism/03_consistent_tensor.html

其他人都在看

欢迎下载体验OneFlow v0.7.0最新版本:https://github.com/Oneflow-Inc/oneflow/icon-default.png?t=M276https://github.com/Oneflow-Inc/oneflow/

相关推荐

<span class=“js_title_inner“>OpenAI 前首席研究官:AGI 核心突破已实现</span>

归根结底,与人合作时要理解他们的诉求,既要让他们实现创作目标,又要协调所有人的贡献形成整体成果,并持续反复地解决这个问题。但如果他们知道你是在为他们争取最大利益,那么当你要求他们去做那件极其困难且令他们恐惧的事情时,有时你就能帮助他们跨越鸿沟,解决问题,避免他们做出愚蠢的举动,最终获得圆满的结果。,包括在实现方式上的品味,其中可能存在不明显的后果——也许是性能上的隐性影响,也许是用户界面演变过程中难以预见的连锁反应,进而需要改变系统更深层的抽象结构——这些只能依靠人类来完成,目前别无选择。

OneFlow深度学习框架 738

<span class=“js_title_inner“>OpenAI 前首席研究官:AGI 核心突破已实现</span>

归根结底,与人合作时要理解他们的诉求,既要让他们实现创作目标,又要协调所有人的贡献形成整体成果,并持续反复地解决这个问题。但如果他们知道你是在为他们争取最大利益,那么当你要求他们去做那件极其困难且令他们恐惧的事情时,有时你就能帮助他们跨越鸿沟,解决问题,避免他们做出愚蠢的举动,最终获得圆满的结果。,包括在实现方式上的品味,其中可能存在不明显的后果——也许是性能上的隐性影响,也许是用户界面演变过程中难以预见的连锁反应,进而需要改变系统更深层的抽象结构——这些只能依靠人类来完成,目前别无选择。

OneFlow深度学习框架 423

<span class=“js_title_inner“>LLM 推理经济学</span>

作者 | Piotr Mazurek & Felix GabrielOneFlow 编译翻译|张雪聃题图由 SiliconCloud 生成当前 LLM 公司的主要商业模式是通过 API 提供模型访问服务,推理成本结构是决定其盈利能力的关键。本文将从底层原理阐释大模型托管/服务的成本来源、单个 GPU 可生成的词元(Token)数量及其成因,本文以开源模型 LLaMA 3.3 为基础,搭建一个简化版的大模型推理运算世界模型,旨在建立关于 LLM 推理的精确直觉认知。大模型推理的经济学影响远超过技术范畴本身。随

OneFlow深度学习框架 643

Databricks“三级跳”:600 亿美元独角兽的战略跃迁

本文以 S 曲线理论为分析框架,深度解构 Databricks 的三次关键战略跃迁,还原了其技术商业化路径中的关键决策——如拒绝本地化部署的短期利益、与微软 Azure 的生态合作以及从开源社区到企业级服务的平衡艺术。Databricks 作为数据与 AI 领域的代表性企业,从开源项目 Spark 起步,逐步发展为估值超 600 亿美元的行业巨头,其成长历程不仅折射出大数据技术的演进史,更揭示了科技公司如何通过连续的战略跃迁实现指数级增长。当时的背景是:互联网的迅速发展催生了海量数据,尤其是非结构化数据。

OneFlow深度学习框架 1220

AI 云服务之争:CoreWeave 向上,Nebius 向下

平台中的大多数工具均为自主研发,包括支持大规模调度的托管 Kubernetes、基于 Slurm 的集群调度系统、用于实验管理的 MLflow、自研的可观测性工具,以及一个具备安全保障的云端基础设施控制平台。它跳过了这一环节,设计了自己的服务器机架,以建立一个更加垂直整合的系统,从数据中心架构到托管式 Kubernetes 服务,再到像 Nebius AI Studio 这样的应用层工具(该工具为 DeepSeek、Llama 和 Flux 这样的开源模型提供推理 API)。的策略更全面和“全栈”。

OneFlow深度学习框架 2120

<span class=“js_title_inner“>AI 云服务之争:CoreWeave 向上,Nebius 向下</span>

平台中的大多数工具均为自主研发,包括支持大规模调度的托管 Kubernetes、基于 Slurm 的集群调度系统、用于实验管理的 MLflow、自研的可观测性工具,以及一个具备安全保障的云端基础设施控制平台。它跳过了这一环节,设计了自己的服务器机架,以建立一个更加垂直整合的系统,从数据中心架构到托管式 Kubernetes 服务,再到像 Nebius AI Studio 这样的应用层工具(该工具为 DeepSeek、Llama 和 Flux 这样的开源模型提供推理 API)。的策略更全面和“全栈”。

OneFlow深度学习框架 157

关于 DeepSeek-R1 API 评测,至少有 7 个误区

xx 58.33%。以某评测者的“人类头发数量的乘积是多少?市面上基本不存在“非满血版 R1”,也基本不存在所谓模型“降智”,而 R1 蒸馏版(70B、1.5B等)与满血版的效果差距很明显,一般知名服务商都会注明,如果这些平台提供所谓“非满血版 R1”,很容易测试出来,这完全是自砸招牌,他们没有动机“以次充好”。可以确定的是,市面上知名的第三方平台部署的都是“满血版 R1(671B)”,之所以用户使用感受有差异,是因为模型输出的随机性、平台提供的配套功能及超参数设置等可能不一致,而非底层模型本身的差异。

OneFlow深度学习框架 1157

<span class=“js_title_inner“>关于 DeepSeek-R1 API 评测,至少有 7 个误区</span>

xx 58.33%。以某评测者的“人类头发数量的乘积是多少?市面上基本不存在“非满血版 R1”,也基本不存在所谓模型“降智”,而 R1 蒸馏版(70B、1.5B等)与满血版的效果差距很明显,一般知名服务商都会注明,如果这些平台提供所谓“非满血版 R1”,很容易测试出来,这完全是自砸招牌,他们没有动机“以次充好”。可以确定的是,市面上知名的第三方平台部署的都是“满血版 R1(671B)”,之所以用户使用感受有差异,是因为模型输出的随机性、平台提供的配套功能及超参数设置等可能不一致,而非底层模型本身的差异。

OneFlow深度学习框架 139

首发!硅基流动 x 华为云联合推出基于昇腾云的 DeepSeek R1 & V3 推理服务

DeepSeek-R1、DeepSeek-V3 开源后引发全球震动,它们是深度求索团队为全人类献上的一份大礼,我们由衷为他们取得的成功感到高兴。经过硅基流动和华为云团队连日攻坚,今天,我们也为国内用户献上春节礼物:大模型云服务平台 SiliconCloud 首发上线基于华为云昇腾云服务的 DeepSeek-V3、DeepSeek-R1。 需要特别强调的是,无论是在昇腾上适配 DeepSeek-R...

OneFlow深度学习框架 4249

<span class=“js_title_inner“>首发!硅基流动 x 华为云联合推出基于昇腾云的 DeepSeek R1 &amp; V3 推理服务</span>

5. 与 DeepSeek 官方优惠期价格保持一致,SiliconCloud 上的 DeepSeek-V3 的优惠期价格(截止 2 月 8 日 24:00)为 ¥1 / M tokens(输入)& ¥2 / M tokens (输出),DeepSeek-R1 的价格为 ¥4 / M tokens(输入)& ¥16 / M tokens (输出)。华为云昇腾云服务可提供澎湃、弹性、充足的算力。双模型,还是在此前上线其他模型的过程中,我们都得到了 DeepSeek 与华为云的大力支持,向他们致以深深的谢意与。

OneFlow深度学习框架 142

站在 AI 十字路口:直面智能体与机器人狂潮

生成式 AI 正在迅速介入人类世界。随着 AI 大模型不断进步,我们正面临着巨大的社会变革。本文探讨了一个由高度智能的智能体和机器人主导的世界——AI 亚特兰蒂斯世界(指在数字领域拥有几乎无限的 AI 资源)。本文分析了当前的 AI 模型为何从根本上不同于以往的技术范式,它们如何重塑各行业,以及人类面临的关键选择。作者认为,每个人都必须理解 AI 的进步将如何重塑经济、创造力和人类潜力的规则。除...

OneFlow深度学习框架 605

<span class=“js_title_inner“>站在 AI 十字路口:直面智能体与机器人狂潮</span>

拥有 AI 的政府能够提供更好的服务,制定数据驱动的政策,增强国家安全,并为民众提供更高的透明度和问责制。这里有一个令人难以置信的想法:你现在可以雇佣智能体员工和机器人工作者,他们的薪水甚至低于你花在咖啡上的费用——然而他们却拥有与普通大学毕业生相同的技能。拥有更多的闲暇时间当然不错,但真正的关键是,AI 为你腾出了精神空间,让你能专注于更大的挑战。像 ComfyUI 这样的工具是这一转变的早期例子——人们现在可以创建复杂的 AI 工作流,其中信息和任务从一个阶段无缝流向另一个阶段。这些模型是通用翻译器。

OneFlow深度学习框架 96

迈向 AI 驱动型经济:当资本不需要打工人

AI 的迅猛发展与普及,引发了学术界与工业界对现在的社会结构与经济模型的根本性反思。此前 OneFlow 发布的《资本、AGI 与人类雄心》、《AGI 时代的智能诅咒》、《AGI 崛起之后:重构经济模式与社会结构》三篇文章,深入探讨了劳动力替代性 AI 改变人力与非人力生产要素的相对重要性后,给整个社会带来的好处与风险,以及应对挑战的解决方案。在本文中,借助一众经济学家和科幻作家的理论观点,Int...

OneFlow深度学习框架 1026

<span class=“js_title_inner“>迈向 AI 驱动型经济:当资本不需要打工人</span>

相对而言,伊恩·M·班克斯(https://en.wikipedia.org/wiki/Iain_Banks)的《文明》系列丛书则描绘了一个后匮乏的社会,在这个社会中,拥有感知能力的 “AI 智能体(AI Minds)” 管理着星舰和栖息地,根据需求分配资源,而非货币。例如,《星际迷航》中,技术(如复制器和先进的星舰 AI )消除了物质匮乏,推动了一个专注于探索和文化发展的社会。而 AI 的引入,使非人类智能体成为生产力的主要推动者,将剩余价值的核心转移到了算法、数据集和计算系统的所有权上。

OneFlow深度学习框架 107

AGI 崛起之后:重构经济模式与社会结构

AGI 日益临近,这也迫使我们重新审视现有的社会结构和经济模式。在《资本、AGI 与人类雄心》一文中,Rudolf 指出,AGI 到来之后,劳动力替代性 AI 将改变人力与非人力生产要素的相对重要性,这会降低社会对人类的关注度,同时使现有的权力更加有效和根深蒂固。基于资源诅咒现象,Luke Drago 在《AGI 时代的智能诅咒》一文中将 Rudolf 的上述核心观点描述为智能诅咒。他认为,智能诅...

OneFlow深度学习框架 1310

AGI 时代的智能诅咒

经济学研究指出,依赖出售石油等自然资源获得收入而非公民税收的国家,会受到资源诅咒的影响,结果是权贵们攫取了大量财富,却停止了对普通人的投资。当 AGI 实现后,人类是否会面临类似的“智能诅咒”?在此前发布的《资本、AGI 与人类雄心》一文中,Rudolf 指出,AGI 到来之后,劳动力替代性 AI 将改变人力与非人力生产要素的相对重要性,这会降低社会对人类的关注度,同时使现有的权力更加有效和根深蒂...

OneFlow深度学习框架 766

资本、AGI 与人类雄心

通用人工智能(AGI)正在不可阻挡地走向人类社会,有人期待它带来的巨大生产力突破,有人也在警告它可能成为洪水猛兽,也有人对它的发展只是持观望态度。不管怎样,基于你所了解的信息,你可以尝试描绘一幅 AGI 图景,并畅想它在人类政治、经济、社会发展会产生的影响。在本文中,作者 Rudolf 深入剖析了 AGI 到来之后的资本、人类劳动与社会权力结构之间的复杂关系。其核心观点是,劳动力替代性 AI 将改...

OneFlow深度学习框架 886

2025 年 AI 十大展望:软件市场扩大 10 倍、系统比模型更重要、OpenAI 先发优势消退...

尽管有 Scaling Law 放缓这样的疑虑,但整体而言,多数业内人士对AI过去一年的诸多进展感到兴奋,对新的一年 AI 的发展更是充满期待,尽管他们对未来的预测可能不尽相同。在本文中,Foundation Capital 合伙人Ashu Grag回顾了 2024 年 AI 发展的里程碑事件,并重点介绍了 2025 年的前景,主要包括:1.预训练局限将推动新的 AI 突破2.AI系...

OneFlow深度学习框架 1590

900页免费“生成式AI与大模型”电子书|OneFlow年货

难以想象,如果不是Scaling Law放缓,2024年AI领域会发生哪些惊人变化,但你可能又会感到庆幸,正是由于Scaling Law放缓,它给了这个行业的后来者们追赶的机会,也给了更多普通人搭乘这一轮技术革命的机会。AI领域的变化激荡人心。一年前,AI社区普遍认为,与OpenAI的模型相比,大部分模型与它有半年或一年的差距,但大模型预训练逐渐没有秘密,它的入局门槛数量级降低。令国内外科技界深感...

OneFlow深度学习框架 659

2024年AI盘点:投资高歌猛进、基础设施重构、技术采用加速

本文全面分析了今年AI 的发展,包含AI 技术栈的基础设施层、基础模型层、应用层、工具层,尤其是各个层面的主要收获、值得关注的趋势与值得关注的初创公司。此外,本文还概括了 AI 领域的投资和并购情况,以及其他AI 趋势。需要指出的是,可能限于作者KelvinMu的关注重心,本文除了对中国基础模型的介绍,没有更多关于中国 AI 其他技术栈的进展。不过,这并不妨碍本文仍是我们了解 2024...

OneFlow深度学习框架 2010

从零实现极速LLM推理

作者 | Andrew Chen翻译|张雪聃、刘乾裕OneFlow编译题图由SiliconCloud平台生成本文旨在从零开始,仅使用C++和CUDA构建一个大语言模型(LLM)推理引擎,且不借助其他外部库。为何要这样做?通过这种方式,我们能够全面了解LLM推理的整个技术栈——这一点正变得日益重要[1]——从CUDA kernel到模型架构,并且切实体会不同的优化方式如何影响推理速度。其中一个最为重...

OneFlow深度学习框架 1207

AI数据中心历史、技术与关键企业

过去一年,通过模型架构创新、更优质训练数据和更大算力规模来训练模型,顶尖大模型之间的性能差距急剧缩小。如果Scaling law依然有效,为了进一步扩展模型规模与性能,在模型架构创新存在极大不确定性情况下,通过获得更多的算力来建造全新的数据中心,从而更快地训练模型以取得领先地位,这是确定性更高的收益。目前,以马斯克xAI为代表的大模型公司,快速部署了10万台GPU集群,成为数据中心扩展的领头羊,可...

OneFlow深度学习框架 971

红杉资本2025年AI三大展望:大模型厂商各显神通;杀手级应用AI搜索;AI支出变稳...

2024年,随着Scaling law放缓,AI领域在大模型、基础设施上的能力稳步提升,尽管应用层出现了各种有趣的探索,但是更大的潜能有待挖掘。2025年即将来临,将出现哪些变化?近日,红杉资本投资人David Cahn对2024年AI领域的发展作了简要总结并对2025年作了三大预测。他认为,今年是AI发展的萌芽期,其构建基石已经稳固建立,只待2025年发芽结果。他在本文中阐述了三点预言:首次,基...

OneFlow深度学习框架 1675

AI半导体技术、市场与未来

过去两年,英伟达崛起是科技领域的一个经典案例。通过CUDA系统,他们创建了一个使用GPU进行机器学习的开发者生态系统;通过Mellanox,他们成为了数据中心网络的领导者。然后,他们将所有硬件集成到服务器中,提供垂直集成的算力一体机。凭借这一系列组合性技术优势,英伟达在“AI淘金热”中提供的铲子占据行业核心地位,这导致它成为有史以来最成功的公司之一。随之而来的是,不少挑战者入局以求从英伟达主导的市...

OneFlow深度学习框架 1万+

比GPU快20倍?d-Matrix推理性价比分析

AI推理算力需求正在大幅增长。一方面,像硅基流动、Fireworks这样的AI基础设施软件公司通过软件层面的优化以提供高性价比的大模型推理服务,另一方面,以Cerebras、Groq为代表的芯片公司相继推出了专用AI推理芯片,通过硬件层面的创新,以数量级的推理速度与成本优势来挑战英伟达GPU的市场地位。AI推理芯片市场的竞争者还在增加。近日,成立于2019年的硅谷推理芯片创业公司d-Matrix的...

OneFlow深度学习框架 2035

生成式AI推理技术、市场与未来

OpenAI o1、QwQ-32B-Preview、DeepSeek R1-Lite-Preview的相继发布,预示着生成式AI研究正从预训练转向推理(Inference),以提升AI逻辑推理(reasoning)能力,这一转变将极大推动上层应用的发展。红杉资本近期指出,在可预见的未来,逻辑推理和推理时计算将是一个重要主题,并开启生成式AI的下一阶段。新一轮竞赛已然开始。那么,在推理这一新兴市场,...

OneFlow深度学习框架 2091

50张图,直观理解混合专家(MoE)大模型

Mixtral 8x7B的高效训练与推理效果曾引发AI社区对混合专家(MoE)模型的广泛关注,后来居上的国产开源大模型De‍epSeek以及腾讯近期开源的Hunyuan-Large(基于Transformer的最大MoE模型)也选择了MoE框架路线。为何大语言模型总是离不开MoE的身影?借助50多个图例,数据科学家Maarten Grootendorst由浅入深多维度剖析了MoE模型,从基础概念出...

OneFlow深度学习框架 9529

LLM后训练绝招:1%预训练成本,实现最高20倍算力扩展效果

根据规模定律,扩大训练计算规模可以提高大型语言模型(LLM)性能的关键,但调研机构Epoch AI的研究,LLM再训练无需高额费用,也能让AI能力获得显著提升。在该研究中,他们引入了一个基本框架,用于量化后训练增强的收益和成本,特别是通过计算等效增益来衡量收益。他们将该框架应用于一系列具有代表性的后训练增强,并发现性能提升非常显著,但微调成本通常与预训练成本相比非常小,某些后训练增强技术可以在不到...

OneFlow深度学习框架 4620

LLM逻辑推演策略选择:推理时计算 vs 训练时计算

AGI实现的一大标志是,具备人类级别的逻辑推理(reasoning)能力。近期,随着推理(inference)模型GPT o1、DeepSeek R1-Lite的发布,模型的逻辑推理能力得到显著提升,也预示着对LLM潜力的深度挖掘正在转向推理阶段。围绕增强LLM逻辑推理能力这一目标,美国人工智能与密码学研究实验室Bagel团队结合最新研究,从算术、常识和符号这三种主要逻辑推理类型出发,对比了在推理...

OneFlow深度学习框架 4206

探索AI框架前沿|OneFlow招聘深度学习研发工程师(实习)

一、岗位名称:深度学习研发工程师-框架开发方向(实习)岗位职责1. 参与 OneFlow 框架开发、重构与性能优化;2. 参与深度学习编译、高阶自动微分等深度学习框架相关技术演进工作。岗位要求1. 计算机或电子通信相关专业,本科及以上学历;2. 具备C/C++、Python编程基础,有良好的软件开发素养,熟悉TDD、CI/CD、敏捷开发流程等;3. 了解深度学习模型,有一定机器学习基础;4. 熟悉...

OneFlow深度学习框架 571
上一篇: 王世杰:读博被美国拒签之后|OneFlow U
下一篇: 我,机器学习工程师,决定跑路了
OneFlow深度学习框架
OneFlow深度学习框架 企业官方账号 企业官方账号
博客等级 码龄6年 5868粉丝 385原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值