从零开始自制深度学习框架

从零开始手写机器学习框架:我的深度学习之旅——开启深度学习的底层探索 本文将带你开启一场从零开始手写机器学习框架的深度学习之旅,深入探索深度学习的核心概念、关键技巧,并通过详细的代码案例分析,让你亲身体验构建一个简单深度学习框架的过程。此外,深度学习框架将与更多的领域进行融合,如生物科学、材料科学等,为这些领域的研究和发展提供强大的支持。本文将进一步深入,构建一个更复杂、功能更丰富的深度学习框架,涵盖更多类型的网络层(如卷积层、池化层)、优化算法(如Adam优化器),并通过详细的代码案例分析展示这些组件的实现和应用。 阅读详情

一、深度学习数据结构设计

在构建深度学习框架时,数据结构是整个框架设计的起点。本章选择 ndarray 作为底层基础,其原因在于,它已经提供了成熟且高效的多维数组计算能力,包括向量化运算、广播机制以及底层的高性能实现。这使得我们无需重新实现底层数值计算,就可以直接获得接近工业级的计算效率,从而将精力集中在框架层的设计上。然而,深度学习并不仅仅是数值计算问题,还涉及“计算过程的管理”。例如,张量之间的依赖关系、梯度传播路径、以及模型参数的更新机制,这些都是 ndarray 本身所不具备的能力。换言之,ndarray 只解决了“算得快”的问题,但没有解决“怎么学”的问题。因此,本章在 ndarray 的基础上进行封装,引入 Tensor 这一新的数据结构,使其不仅保留高效的数值计算能力,同时也具备进入深度学习计算系统所需的语义扩展能力。通过这一改造,数据从单纯的数值载体,升级为能够参与计算图与学习过程的基本单元。

1.1 Tensor数据结构

Tensor的最底层必须依赖ndarray,因为所有的计算都由它完成。因此,Tensor的第一步完成ndarray封装。

class Tensor:
    def __init__(self, data):
        self.data = data

二、基于 Tensor 的计算设计

深度学习框架的本质不仅在于“存储数据”,更在于“定义计算过程”。因此,仅有 Tensor 这一数据结构是不够的,还需要在其之上进一步设计一套基于 Tensor 的计算机制,使数据能够在不同操作之间流动、组合,并逐步构建为可用于学习的计算结构。

2.1 Function 的结构设计

本章通过引入类来封装计算过程,其核心目的在于将神经网络的前向传播与反向传播过程统一为可管理的对象结构,使计算过程能够被保存、追踪与复用,从而为后续的自动求导与模型优化提供基础。

2.1.1 前向与反向传播

class Function:
    def __call__(self, input):
        x = input.data
        y = self.forward(x)
        output = Tensor(y)
        return output
        
    def forward(self, x):
        raise NotImplementedError
        
    def backward(self, dy):
        raise NotImplementedError
        
        
class Exp(Function):
    def forward(self, x):
        return np.exp(x)
    
    def backward(self, dy):
        x = self.input.data
        return np.exp(x)

2.2 单变量自动求导

在这里插入图片描述

2.2.1 单变量计算图

从上图可以看出,一个变量(Variable)需要存储三个部分:ndarray、梯度以及生成该变量的函数,它们各自的作用如下:

  • ndarray:用于存储变量的实际数据,是前向传播过程中参与各种数学运算的对象。
  • 梯度:用于存储损失函数对该变量的偏导数。
  • 生成该变量的函数:用于记录生成当前变量的函数对象。当执行反向传播时,可以根据该函数找到当前变量的来源,并调用相应的反向传播方法计算输入变量的梯度,从而将梯度沿计算图逐层传递,实现自动求导。
class Tensor:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")
                
        self.data = data
        self.grad = None
        self.creator = None
        
    def set_creator(self, func):
        self.creator = func
        
    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)
    
        func = [self.creator]
        while func:
            f = func.pop()
            x, y = f.input, f.output
            x.grad = f.backward(y.grad)
            if x.creator is not None:
                func.append(x.creator)
             
class Function:
    def __call__(self, input):
        x = input.data
        y = self.forward(x)
        output = Tensor(as_ndarray(y))
        output.set_creator(self)
        self.input = input
        self.output = output
        return output
        
    def forward(self, x):
        raise NotImplementedError
        
    def backward(self, dy):
        raise NotImplementedError
        
class Exp(Function):
    def forward(self, x):
        return np.exp(x)
    
    def backward(self, dy):
        x = self.input.data
        return np.exp(x)
        
def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x

2.3 多变量自动求导

前面介绍的自动求导仅适用于单变量计算,而实际的深度学习模型通常包含多个输入变量,各变量之间存在复杂的计算关系。为了能够处理更加复杂的计算图,并实现神经网络中多输入的梯度计算,需要将自动求导机制从单变量扩展到多变量。因此,本节将在单变量自动求导的基础上,引入多变量自动求导,使其能够支持更加复杂的数学运算。

2.3.1 可变参数

本章节之前涉及的函数,其输入、输出都只有一个变量,但有些函数需要多个变量输入,例如加法运算和乘法运算。可考虑到这些情况,我们需要将单变量自动求导,处理成可变输入和输出。可变意味着参数的数量是可以变化的,数量可以是不小于1的整数值。

class Tensor:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")
                
        self.data = data
        self.grad = None
        self.creator = None
        
    def set_creator(self, func):
        self.creator = func
        
    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)
    
        func = [self.creator]
        while func:
            f = func.pop()
            dys = [output.grad for output in f.outputs]
            dxs = f.backward(*dys)
            
            if not isinstance(dxs, tuple):
                dxs = (dxs,)
            
            for x, dx in zip(f.inputs, dxs):
                x.grad = dx
            
                if x.creator is not None:
                    func.append(x.creator)
             
class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)
        
        if not isinstance(ys, tuple):
            ys = (ys,)
            
        outputs = [Tensor(as_ndarray(y)) for y in ys]
        if output in outputs:
            output.set_creator(self)
            
        self.inputs = inputs
        self.outputs = outputs
        return outputs if len(outputs) > 1 else outputs[0]
        
    def forward(self, *x):
        raise NotImplementedError
        
    def backward(self, dy):
        raise NotImplementedError
        
        
class Exp(Function):
    def forward(self, x):
        return np.exp(x)
    
    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)
        
class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y
        
    def backward(self, dy):
        return dy, dy
        
def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x

2.3.2 重复变量

(1) 问题描述
相同变量进行加法运算时,不能正确求导。下面是测试结果:

if __name__ == "__main__":
    x = Tensor(np.array(3.0))
    y = add(x, x)
    print(y.data)

    y.backward()
    print(x.grad)
    
测试结果:
6.0
1

(2) 优化方法

class Tensor:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")
                
        self.data = data
        self.grad = None
        self.creator = None
        
    def set_creator(self, func):
        self.creator = func
        
    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)
    
        func = [self.creator]
        while func:
            f = func.pop()
            dys = [output.grad for output in f.outputs]
            dxs = f.backward(*dys)
            
            if not isinstance(dxs, tuple):
                dxs = (dxs,)
            
            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:  
                    x.grad = dx
                else:
                    x.grad = x.grad + dx
            
                if x.creator is not None:
                    func.append(x.creator)
             
class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)
        
        if not isinstance(ys, tuple):
            ys = (ys,)
            
        outputs = [Tensor(as_ndarray(y)) for y in ys]
        if output in outputs:
            output.set_creator(self)
            
        self.inputs = inputs
        self.outputs = outputs
        return outputs if len(outputs) > 1 else outputs[0]
        
    def forward(self, *x):
        raise NotImplementedError
        
    def backward(self, dy):
        raise NotImplementedError
        
        
class Exp(Function):
    def forward(self, x):
        return np.exp(x)
    
    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)
        
class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y
        
    def backward(self, dy):
        return dy, dy
        
def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x

2.3.3 导数重置

(1) 问题描述
相同变量进行不同计算时,不能正确求导。下面是测试结果:

if __name__ == "__main__":
    # 第一个计算
    x = Tensor(np.array(3.0))
    y = add(x, x)
    y.backward()
    print(x.grad)

    # 第二个计算
    y = add(add(x, x), x)
    y.backward()
    print(x.grad)
    
测试结果:
2
5    

(2) 优化方法

class Tensor:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")
                
        self.data = data
        self.grad = None
        self.creator = None
        
    def set_creator(self, func):
        self.creator = func
        
    def zero_grad(self):
        self.grad = None  # 进行程序前,对梯度清零
        
    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)
    
        funcs = [self.creator]
        while funcs:
            f = funcs.pop()
            dys = [output.grad for output in f.outputs]
            dxs = f.backward(*dys)
            
            if not isinstance(dxs, tuple):
                dxs = (dxs,)
            
            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:  
                    x.grad = dx
                else:
                    x.grad = x.grad + dx
            
                if x.creator is not None:
                    func.append(x.creator)
             
class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)
        
        if not isinstance(ys, tuple):
            ys = (ys,)
            
        outputs = [Tensor(as_ndarray(y)) for y in ys]
        for output in outputs:
            output.set_creator(self)
            
        self.inputs = inputs
        self.outputs = outputs
        return outputs if len(outputs) > 1 else outputs[0]
        
    def forward(self, *x):
        raise NotImplementedError
        
    def backward(self, dy):
        raise NotImplementedError
        
        
class Exp(Function):
    def forward(self, x):
        return np.exp(x)
    
    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)
        
class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y
        
    def backward(self, dy):
        return dy, dy
        
def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x

2.3.4 传播顺序

前面我们处理的都是笔直得计算图,然而变量与函数并不局限于这种简单得连接方式。我们可以建立复杂的连接,但是不能正确求出这种连接方式的导数,即不能反向传播。
在这里插入图片描述

从图2.2可以看出,变量 a 是后续两个分支(B 和 C)的共同输入,因此其梯度应当是两条传播路径梯度的累加。然而,图中的导数传播顺序与神经网络反向传播的实际过程并不一致,不能正确反映梯度沿计算图逐层反向传播的特点。
在这里插入图片描述
本章节在正向传播中实现层级设置,然后在反向传播中,按照从最高层级到最低层级的顺序取函数。修改完成后,无论计算图多么复杂,反向传播都会按照正确的顺序进行。

class Tensor:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")
                
        self.data = data
        self.grad = None
        self.creator = None
        self.layers = 0   # 层级
        
    def set_creator(self, func):
        self.creator = func
        self.layers = func.layers + 1
        
    def zero_grad(self):
        self.grad = None  # 进行程序前,对梯度清零
        
    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)
        
        # 优先队列法(未实现)
        funcs, seen_set = [], set()
        def add_func(f):
            if f not in seen_set:
                funcs.append(f)
                seen_set.add(f)  # 防止重复调用
                funcs.sort(key=lambda x: x.layers)
                
        add_func(self.creator)
        while funcs:
            f = funcs.pop()
            dys = [output.grad for output in f.outputs]
            dxs = f.backward(*dys)
            
            if not isinstance(dxs, tuple):
                dxs = (dxs,)
            
            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:  
                    x.grad = dx
                else:
                    x.grad = x.grad + dx
            
                if x.creator is not None:
                    add_func(x.creator)
             
class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)
        
        if not isinstance(ys, tuple):
            ys = (ys,)
            
        outputs = [Tensor(as_ndarray(y)) for y in ys]
        
        # 通过输入变量层级记录函数层级
        self.layers = max([input.layers for input in inputs])  
        for output in outputs:
            output.set_creator(self)
            
        self.inputs = inputs
        self.outputs = outputs
        return outputs if len(outputs) > 1 else outputs[0]
        
    def forward(self, *x):
        raise NotImplementedError
        
    def backward(self, dy):
        raise NotImplementedError
        
        
class Exp(Function):
    def forward(self, x):
        return np.exp(x)
    
    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)
        
class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y
        
    def backward(self, dy):
        return dy, dy
        
def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x

2.4 内存管理

我们写的 Python 代码本身不能直接被计算机执行,需要通过一个叫 Python 解释器 的程序来运行它。最常用的解释器是 CPython,它是用 C 语言实现的,也是 Python 官方默认的实现方式。简单来说,Python 负责让我们方便地编写程序,而 CPython 负责帮助计算机理解并执行这些程序。Cpython使用两种方式管理内存:一种引用计数,另一种是分代垃圾回收。

2.4.1 引用计数法

GC 是一种“释放怎么都无法被引用的对象的机制”。显然,可以让所有对象事先记录下“有多少程序引用自己”。让各对象知道自己的“人气指数”,从而让没有人气的对象自己消失,这就是引用计数法,它是George E. Collins于1960年钻研出来的。

2.4.2 分代垃圾回收

分代垃圾回收在对象中导入“年龄”的概念,通过优先回收容易成为垃圾的对象,提高垃圾回收效率。人们从众多程序案例中总结出一个经验:“大部对象在生成后马上变成垃圾”,很少有对象活得很久。分代垃圾回收利用该经验,在对象中导入年龄概念,经历一次GC后,活下来的对象年龄为1岁。Python的分代回收是一种垃圾回收策略,其核心思想是对象存活时间越长,以后存活的概率越大,新创建的对象更容易成为垃圾。因此,Python不会每次都扫描所有对象,而把对象按年龄划分,提高垃圾回收效率。
在这里插入图片描述

2.4.3 弱引用

对象的弱引用不能保证对象存活:当所指对象的引用只剩弱引用时, 垃圾回收 可以销毁所指对象,并将其内存重新用于其它用途。但是,在实际销毁对象之前,即使没有强引用,弱引用也能返回该对象。

import weakref
import numpy as np

class Tensor:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")
                
        self.data = data
        self.grad = None
        self.creator = None
        self.layers = 0   # 层级
        
    def set_creator(self, func):
        self.creator = func
        self.layers = func.layers + 1
        
    def zero_grad(self):
        self.grad = None  # 进行程序前,对梯度清零
        
    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)
        
        # 优先队列法(未实现)
        funcs, seen_set = [], set()
        def add_func(f):
            if f not in seen_set:
                funcs.append(f)
                seen_set.add(f)  # 防止重复调用
                funcs.sort(key=lambda x: x.layers)
                
        add_func(self.creator)
        while funcs:
            f = funcs.pop()
            dys = [output().grad for output in f.outputs]
            dxs = f.backward(*dys)
            
            if not isinstance(dxs, tuple):
                dxs = (dxs,)
            
            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:  
                    x.grad = dx
                else:
                    x.grad = x.grad + dx
            
                if x.creator is not None:
                    add_func(x.creator)
             
class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)
        
        if not isinstance(ys, tuple):
            ys = (ys,)
            
        outputs = [Tensor(as_ndarray(y)) for y in ys]
        
        # 通过输入变量层级记录函数层级
        self.layers = max([input.layers for input in inputs])  
        for output in outputs:
            output.set_creator(self)
            
        self.inputs = inputs
        self.outputs = [weakref.ref(output) for output in outputs]
        return outputs if len(outputs) > 1 else outputs[0]
        
    def forward(self, *x):
        raise NotImplementedError
        
    def backward(self, dy):
        raise NotImplementedError
        
        
class Exp(Function):
    def forward(self, x):
        return np.exp(x)
    
    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)
        
class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y
        
    def backward(self, dy):
        return dy, dy
        
def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x

2.4.4 清除无用导数

在深度学习中,只有终端变量的导数需要通过反向传播求得。中间变量的导数几乎用不到。因此,我们可以增加一种机制消除中间变量的导数。

import weakref
import numpy as np


class Tensor:
    def __init__(self, data):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")

        self.data = data
        self.grad = None
        self.creator = None
        self.layers = 0  # 层级

    def set_creator(self, func):
        self.creator = func
        self.layers = func.layers + 1

    def zero_grad(self):
        self.grad = None  # 进行程序前,对梯度清零

    def backward(self, retain_grad=False):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)

        # 优先队列法(未实现)
        funcs, seen_set = [], set()

        def add_func(f):
            if f not in seen_set:
                funcs.append(f)
                seen_set.add(f)  # 防止重复调用
                funcs.sort(key=lambda x: x.layers)

        add_func(self.creator)
        while funcs:
            f = funcs.pop()
            dys = [output().grad for output in f.outputs]
            dxs = f.backward(*dys)

            if not isinstance(dxs, tuple):
                dxs = (dxs,)

            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:
                    x.grad = dx
                else:
                    x.grad = x.grad + dx

                if x.creator is not None:
                    add_func(x.creator)

            if not retain_grad:
                for output in f.outputs:
                    output().grad = None


class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)

        if not isinstance(ys, tuple):
            ys = (ys,)

        outputs = [Tensor(as_ndarray(y)) for y in ys]

        # 通过输入变量层级记录函数层级
        self.layers = max([input.layers for input in inputs])
        for output in outputs:
            output.set_creator(self)

        self.inputs = inputs
        self.outputs = [weakref.ref(output) for output in outputs]
        return outputs if len(outputs) > 1 else outputs[0]

    def forward(self, *x):
        raise NotImplementedError

    def backward(self, dy):
        raise NotImplementedError


class Exp(Function):
    def forward(self, x):
        return np.exp(x)

    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)


class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y

    def backward(self, dy):
        return dy, dy


def add(x0, x1):
    return Add()(x0, x1)


def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x


if __name__ == "__main__":
    x_0 = Tensor(np.array(1.0))
    x_1 = Tensor(np.array(1.0))

    t = add(x_0, x_1)
    y = add(x_0, t)

    y.backward()
    print(y.grad, t.grad)
    print(x_0.grad, x_1.grad)

2.5 模式切换

神经网络训练阶段既需要执行正向传播,也需要完成反向传播以计算参数梯度;而模型推理阶段仅需前向计算,无需构建梯度计算图、执行反向传播,因此需要临时关闭自动微分功能。本文借助 Python 的with上下文语句,实现 “禁用反向传播” 模式的临时切换。

import weakref
import contextlib
import numpy as np


class Tensor:
    def __init__(self, data, name=None):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")

        self.data = data
        self.grad = None
        self.name = name
        self.creator = None
        self.layers = 0  # 层级

    def set_creator(self, func):
        self.creator = func
        self.layers = func.layers + 1

    def clear_grad(self):
        self.grad = None

    def backward(self, retain_grad=False):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)

        # 优先队列法(未实现)
        funcs, seen_set = [], set()

        def add_func(f):
            if f not in seen_set:
                funcs.append(f)
                seen_set.add(f)  # 防止重复调用
                funcs.sort(key=lambda x: x.layers)

        add_func(self.creator)
        while funcs:
            f = funcs.pop()
            dys = [output().grad for output in f.outputs]
            dxs = f.backward(*dys)

            if not isinstance(dxs, tuple):
                dxs = (dxs,)

            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:
                    x.grad = dx
                else:
                    x.grad = x.grad + dx

                if x.creator is not None:
                    add_func(x.creator)

            if not retain_grad:
                for output in f.outputs:
                    output().grad = None


class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)

        if not isinstance(ys, tuple):
            ys = (ys,)

        outputs = [Tensor(as_ndarray(y)) for y in ys]

        if Config.enable_backprop:
            # 通过输入变量层级记录函数层级
            self.layers = max([input.layers for input in inputs])
            for output in outputs:
                output.set_creator(self)

            self.inputs = inputs
            self.outputs = [weakref.ref(output) for output in outputs]
        return outputs if len(outputs) > 1 else outputs[0]

    def forward(self, *x):
        raise NotImplementedError

    def backward(self, dy):
        raise NotImplementedError


class Exp(Function):
    def forward(self, x):
        return np.exp(x)

    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)


class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y

    def backward(self, dy):
        return dy, dy


def add(x0, x1):
    return Add()(x0, x1)


def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x


class Config:
    enable_backprop = True


@contextlib.contextmanager
def using_config(name, value):
    old_value = getattr(Config, name)
    setattr(Config, name, value)
    try:
        yield
    finally:
        setattr(Config, name, old_value)


def zero_grad():
    return using_config("enable_backprop", False)


if __name__ == "__main__":
    x_0 = Tensor(np.array(1.0))
    x_1 = Tensor(np.array(1.0))

    t = add(x_0, x_1)
    y = add(x_0, t)

    y.backward()
    print(y.grad, t.grad)
    print(x_0.grad, x_1.grad)

2.6 属性扩展

为了省去频繁书写.data 的冗余代码、对齐 PyTorch 的使用习惯,我们基于底层存储的 numpy 数组,借助 @property 扩展了 shape、ndim、size、dtype 四个只读属性,同时重载__len__、__repr__魔法方法。

import weakref
import contextlib
import numpy as np

class Tensor:
    def __init__(self, data, name=None):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")

        self.data = data
        self.grad = None
        self.name = name
        self.creator = None
        self.layers = 0  # 层级

    @property
    def shape(self):
        return self.data.shape

    @property
    def ndim(self):
        return self.data.ndim

    @property
    def size(self):
        return self.data.size

    @property
    def dtype(self):
        return self.data.dtype

    def __len__(self):
        return len(self.data)

    def __repr__(self):
        if self.data is None:
            return 'Tensor(None)'
        return f'Tensor({str(self.data)}, {self.data.dtype})'

    def set_creator(self, func):
        self.creator = func
        self.layers = func.layers + 1

    def clear_grad(self):
        self.grad = None

    def backward(self, retain_grad=False):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)

        # 优先队列法(未实现)
        funcs, seen_set = [], set()

        def add_func(f):
            if f not in seen_set:
                funcs.append(f)
                seen_set.add(f)  # 防止重复调用
                funcs.sort(key=lambda x: x.layers)

        add_func(self.creator)
        while funcs:
            f = funcs.pop()
            dys = [output().grad for output in f.outputs]
            dxs = f.backward(*dys)

            if not isinstance(dxs, tuple):
                dxs = (dxs,)

            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:
                    x.grad = dx
                else:
                    x.grad = x.grad + dx

                if x.creator is not None:
                    add_func(x.creator)

            if not retain_grad:
                for output in f.outputs:
                    output().grad = None


class Function:
    def __call__(self, *inputs):
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)

        if not isinstance(ys, tuple):
            ys = (ys,)

        outputs = [Tensor(as_ndarray(y)) for y in ys]

        if Config.enable_backprop:
            # 通过输入变量层级记录函数层级
            self.layers = max([input.layers for input in inputs])
            for output in outputs:
                output.set_creator(self)

            self.inputs = inputs
            self.outputs = [weakref.ref(output) for output in outputs]
        return outputs if len(outputs) > 1 else outputs[0]

    def forward(self, *x):
        raise NotImplementedError

    def backward(self, dy):
        raise NotImplementedError

class Exp(Function):
    def forward(self, x):
        return np.exp(x)

    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)

class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y

    def backward(self, dy):
        return dy, dy


def add(x0, x1):
    return Add()(x0, x1)

def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x

class Config:
    enable_backprop = True

@contextlib.contextmanager
def using_config(name, value):
    old_value = getattr(Config, name)
    setattr(Config, name, value)
    try:
        yield
    finally:
        setattr(Config, name, old_value)

def zero_grad():
    return using_config("enable_backprop", False)


if __name__ == "__main__":
    x_0 = Tensor(np.array([[1.0, 2.9], [2.0, 9.0]]))
    print(x_0)

2.7 运算符重载

运算符重载的目的是让自定义张量类像普通数字一样,直接使用数学符号运算,代码贴合数学公式、简洁易读,同时统一封装前向、反向传播的计算图逻辑。

import weakref
import contextlib
import numpy as np

class Tensor:
    def __init__(self, data, name=None):
        if data is not None:
            if not isinstance(data, np.ndarray):
                raise TypeError(f"{type(data)} is not supported!")

        self.data = data
        self.grad = None
        self.name = name
        self.creator = None
        self.layers = 0  # 层级

    @property
    def shape(self):
        return self.data.shape

    @property
    def ndim(self):
        return self.data.ndim

    @property
    def size(self):
        return self.data.size

    @property
    def dtype(self):
        return self.data.dtype

    def __len__(self):
        return len(self.data)

    def __repr__(self):
        if self.data is None:
            return 'Tensor(None)'
        return f'Tensor({str(self.data)}, {self.data.dtype})'

    def set_creator(self, func):
        self.creator = func
        self.layers = func.layers + 1

    def clear_grad(self):
        self.grad = None

    def __mul__(self, other):
        return mul(self, other)

    def __add__(self, other):
        return add(self, other)

    def __neg__(self):
        return neg(self)

    def __sub__(self, other):
        return sub(self, other)

    def __truediv__(self, other):
        return div(self, other)

    def __pow__(self, c):
        return power(self, c)

    def backward(self, retain_grad=False):
        if self.grad is None:
            self.grad = np.ones_like(self.grad)

        # 优先队列法(未实现)
        funcs, seen_set = [], set()

        def add_func(f):
            if f not in seen_set:
                funcs.append(f)
                seen_set.add(f)  # 防止重复调用
                funcs.sort(key=lambda x: x.layers)

        add_func(self.creator)
        while funcs:
            f = funcs.pop()
            dys = [output().grad for output in f.outputs]
            dxs = f.backward(*dys)

            if not isinstance(dxs, tuple):
                dxs = (dxs,)

            for x, dx in zip(f.inputs, dxs):
                # 重复值优化
                if x.grad is None:
                    x.grad = dx
                else:
                    x.grad = x.grad + dx

                if x.creator is not None:
                    add_func(x.creator)

            if not retain_grad:
                for output in f.outputs:
                    output().grad = None


class Function:
    def __call__(self, *inputs):
        inputs = [as_tensor(input) for input in inputs]
        xs = [input.data for input in inputs]
        ys = self.forward(*xs)

        if not isinstance(ys, tuple):
            ys = (ys,)

        outputs = [Tensor(as_ndarray(y)) for y in ys]

        if Config.enable_backprop:
            # 通过输入变量层级记录函数层级
            self.layers = max([input.layers for input in inputs])
            for output in outputs:
                output.set_creator(self)

            self.inputs = inputs
            self.outputs = [weakref.ref(output) for output in outputs]
        return outputs if len(outputs) > 1 else outputs[0]

    def forward(self, *x):
        raise NotImplementedError

    def backward(self, dy):
        raise NotImplementedError


class Exp(Function):
    def forward(self, x):
        return np.exp(x)

    def backward(self, dy):
        x = self.inputs[0].data
        return np.exp(x)


class Add(Function):
    def forward(self, x_0, x_1):
        y = x_0 + x_1
        return y

    def backward(self, dy):
        return dy, dy


def add(x0, x1):
    return Add()(x0, x1)


class Mul(Function):
    def forward(self, x_0, x_1):
        y = x_0 * x_1
        return y

    def backward(self, dy):
        x_0, x_1 = self.inputs[0].data, self.inputs[1].data
        return x_1 * dy, x_0 * dy

def mul(x_0, x_1):
    return Mul()(x_0, x_1)


class Neg(Function):
    def forward(self, x):
        return -x

    def backward(self, dy):
        return -dy


def neg(x):
    return Neg()(x)


class Sub(Function):
    def forward(self, x_0, x_1):
        y = x_0 - x_1
        return y

    def backward(self, dy):
        return dy, -dy


def sub(x_0, x_1):
    return Sub()(x_0, x_1)


class Div(Function):
    def forward(self, x_0, x_1):
        y = x_0 / x_1
        return y

    def backward(self, dy):
        x0, x1 = self.inputs[0].data, self.inputs[1].data
        return dy/x1, (-x0/x1**2)*dy


def div(x0, x1):
    return Div()(x0, x1)


class Pow(Function):
    def __init__(self, c):
        self.c = c

    def forward(self, x):
        y = x ** self.c
        return y

    def backward(self, dy):
        x = self.inputs[0].data
        return dy * self.c * x ** (self.c - 1)


def power(x, c):
    return Pow(c)(x)


def as_ndarray(x):
    if np.isscalar(x):
        return np.array(x)
    return x


def as_tensor(tensor):
    if isinstance(tensor, Tensor):
        return tensor
    else:
        return Tensor(np.array(tensor))


class Config:
    enable_backprop = True


@contextlib.contextmanager
def using_config(name, value):
    old_value = getattr(Config, name)
    setattr(Config, name, value)
    try:
        yield
    finally:
        setattr(Config, name, old_value)


def zero_grad():
    return using_config("enable_backprop", False)


if __name__ == "__main__":
    x_0 = Tensor(np.array([0.1, 0.1]))
    x_1 = 2
    print(x_0**2)

三、Python打包

3.1 概述

Python打包在很长一段时间内处于混乱不堪的状态,人们花了很多年才使得这一主题重新变得有组织。一切都从1998年引入的distutils包开始,随后在2003年setuptools对其进行改进。这两个项目开启了一段漫长而有纠结的故事,故事包括派生fork、替代项目与完全重写编写,都想要彻底修复Python打包生态。但是,大部分尝试都没有成功。

幸运的是,这种状态正在逐渐被改变。有一个Python Packaging Authority组织,将秩序和组织性带回到打包生态系统中。PyPA维护的Python打包用户指南(Python Packaging User Guide,https://packaging.python.org)是关于最新打包工具和最佳实践的权威信息来源。由于PyPA的参与,构建发行版已经逐渐弃用egg格式,二十支持使用wheel格式。在Python打包用户指南中,推荐包创建与分发的工具如下。

  • 使用setuptools来定义项目,并创建源代码发行版。
  • 使用wheel而不是egg来创建发行版。
  • 使用twine向PyPI上传包的发行版。

3.2 项目配置

组织大型应用的代码最简单的方法是将其分成几个包。这使得代码更加简单,也更容易理解、维护和修改。这样也使得每个包的可复用性最大化。它们的作用就像组件一样。

  1. setup.py
    对于一个需要被分发的包来说,其根目录包含一个setup.py脚本。它定义了distutils模块中描述的所有元数据,并将其合并为标准的setup()函数调用的参数。虽然distutils 是一个标准库模块,但建议你使用setuptools 包来代替,它对标准的distutils 做了一些改进。
from setuptools import setup


setup(
    name="PyTensor",
    version="1.0.0",
    description="A lightweight deep learning tensor framework built from scratch based on NumPy",
    author="Lee Wen-tsao",
    author_email="liwenchao36@163.com",
    packages=["PyTensor"],
    install_requires=[
        'numpy',
    ],
    python_requires=">=3.11",
)

执行打包:

python setup.py bdist_wheel
从0构建深度学习框架——揭秘深度学习框架的黑箱 本文介绍了从零实现的轻量级深度学习框架Needle,其核心特点包括: 完整的自动微分系统:基于计算图实现反向传播,支持链式法则计算梯度 丰富的张量运算:包含元素级运算、矩阵运算等基本操作 多后端支持:可选择NumPy或自定义NDArray后端 神经网络组件:实现线性层、激活函数等基础模块 优化算法:支持SGD、Adam等常用优化器 Needle框架的设计目标不是替代主流框架,而是通过从零实现帮助开发者理解深度学习底层原理,特别是自动微分和计算图机制。框架代码结构清晰,适合作为学习深度学习核心原理的实践项目。 阅读详情

相关推荐

构建深度学习框架

深度学习框架的学习,并构建深度学习框架。 目录前言Day1:理解神经网络1.机器学习的原理与精髓2.knn3.回归4.损失函数4.深度学习模型如何更新权重5.通过程序实现过拟合和欠拟合6.梯度下降Day2:多层神经网络原理以及神经网络自动求导原理1.拟合直线到拟合曲线2.复杂场景下的激活函数3.反向传播4.反向传播的作用5.拓扑图6.拓扑图的作用7.深度合成深度学习框架Day3:结合所学内容,合并并发布通用框架1.构建深度学习框架(mini-flow)2.处理更加复杂的场景,并发布到互联网总结 前言 什

weixin_45234131的博客 595

计算机自制系列书籍(2025.02.04)

计算机自制系列书籍(2025.02.04)

青少年趣味编程 1420

Auto DL云服务器深度学习环境配置全攻略(从零到运行)

本文提供了一份详尽的Auto DL云服务器深度学习环境配置指南,从零开始指导用户完成注册、选卡、创建实例、配置PyCharm远程开发环境、管理Conda与CUDA依赖,到高效上传下载数据并运行第一个深度学习项目。内容涵盖核心步骤与常见避坑技巧,旨在帮助开发者快速利用云端GPU算力,降低深度学习入门门槛。

weixin_29251567的博客 506

【无标题】

Implement DeZero Framework from scratch DeZero是一种极简的深度学习框架

qq_40712026的博客 138

从 ML 到 DL:什么时候该升级

在机器学习的演化过程中,从传统机器学习(ML)升级到深度学习(DL)是一个重要的决策点。本文从工程实践视角出发,深入探讨机器学习升级到深度学习的时机、评估标准、迁移路径和工业界实践。通过分析最新的技术趋势和成功案例,结合实际代码实现,为读者提供一套完整的ML到DL升级决策框架。文章重点讨论了升级时机评估矩阵、迁移学习加速升级、混合模型架构设计、升级成本收益分析以及工业界成功案例,帮助读者在实际工程环境中做出明智的升级决策。

欢迎来到智能安全前线!这里是AI与信息安全交汇之地。我们以代码为武器,深入大模型攻防实战,破解系统漏洞。追踪OpenAI、Meta前沿动态,用红蓝对抗思维,锻造智能时代的坚盾与利矛。 1149

从零实现深度学习框架——自动求导神器计算图

本文介绍自动求导神器——计算图,从本篇文章开始介绍一个自定义的求导工具。

日积月累,天道酬勤 4197

使用 ESP-DL 深度学习库基于 ESP32-S3 实现手势识别

用户基于乐鑫深度学习库 ESP-DL,能够在 ESP32-S3 上部署高性能的深度学习模型。

乐鑫 Espressif 1万+

AOA(Angle of Arrival,到达角)定位算法及其误差分析的原理和MATLAB仿真_aoa定位算法

如图所示,有基站 S 1 ( x 1 , y 1 , z 1 ) , S 2 ( x 2 , y 2 , z 2 ) S_1(x_1,y_1,z_1),S_2(x_2,y_2,z_2) S1​(x1​,y1​,z1​),S2​(x2​,y2​,z2​),目标 T ( x , y , z ) T(x,y,z) T(x,y,z)。2+(z_1-z_2)^2} step1:两基站间的距离L=(x1−x2)2+(y1​−y2​)2+(z1​−z2​)2。

2401_87555613的博客 1482

深度学习从零开始(1)— 如何学习深度学习?

首先说我个人的结论: (1)假如是为了找工作,或者短时间内解决问题,我建议最好的入门书是:《Python 深度学习》和《Scikit-Learn与TensorFlow机器学习实用指南(影印版)》和《TensorFlow:实战Google深度学习框架(第2版)》,三本书都是该领域的经典,而且难得的是非常易读,看完就可以利用经典的机器学习和深度学习算法解决常见的问题。 (2)假如想长期学习,深入理...

u012995500的博客 2730

vcs中systemverilog和c/c++联合仿真

前言 systemverilog中,可以通过DPI、PLI来与外部其它语言的程序进行交互。PLI又分为三类,TF、ACC和VPI,可以认为是PLI技术的三代演进: 其中VPI(PLI3.0)是TF/ACC的一个超集,目前使用的PLI基本上都是VPI。 与PLI技术相独立的另一个技术就是DPI,DPI技术简化了systemverilog与foreign language的交互步骤,对于开发者相对友好,所以使用相对广泛一些。 本文不对DPI中程序调用/数据交互展开讨论(这部分可以参考绿皮书..

kevindas的博客 1万+

Cartographer从安装到实战:手把手教你运行2D/3D建图与定位

本文提供了一份详尽的Cartographer从零安装到实战应用的完整指南。内容涵盖环境准备、依赖编译、源码构建等关键步骤,并手把手指导读者运行2D/3D官方数据集进行建图与定位测试。文章深入解析了参数配置与自定义数据适配方法,帮助开发者快速掌握这一强大的实时同步定位与建图工具,并将其应用于机器人导航与自动驾驶等实际场景。

rice5的博客 949

用C++从零开始实现的小型深度学习训练框架

是一个基于 C++ 从零实现的轻量级深度学习训练框架,具有 PyTorch 风格的 API,支持 CPU 和 CUDA 运行,可用于神经网络训练与推理,还支持 LLM(如 llama/qwen/mistral 模型)的推理。抓本质:聚焦深度学习的核心流程——数据输入→模型计算→梯度更新→参数迭代,所有模块都围绕这个流程展开,不做无关功能。做减法:去掉冗余依赖和复杂封装,用最简洁的结构实现核心功能,让底层逻辑一目了然。

chen1415886044的博客 1101

保姆级避坑指南:用Keil和J-Flash搞定凌思微LE5010蓝牙芯片的首次程序下载

本文提供了一份详细的保姆级避坑指南,帮助开发者使用Keil和J-Flash工具完成凌思微LE5010蓝牙芯片的首次程序下载。文章重点解决了算法文件混淆、协议栈漏烧和仿真器连接异常等常见问题,并详细介绍了开发环境配置、协议栈烧录和下载接口的物理层奥秘,助力开发者高效完成蓝牙开发任务。

weixin_28739079的博客 466

基于python的深度学习框架有_用Python实现深度学习框架

大咖推荐:复旦大学计算机学院教授邱锡鹏、一流科技创始人兼CEO袁进辉(@老师木)、格灵深瞳创始人兼CEO赵勇、奇虎360集团副总裁邓亚峰联合推荐干货满满:从零开始用Python实现自己的深度学习框架,搭建从逻辑回归到卷积神经网络的各类模型和网络,涵盖模型的训练、评估、保存与部署等工程问题聚焦实战:360智能工程部高级机器学习算法工程师倾力打造这是一本引人入胜的书,它通过由浅入深的讲解让你了解深度学...

weixin_39640767的博客 1169

第01章 Hermes Agent 是什么

如果读者所在的开发团队已经引入了 Cursor、Claude Code 这类 AI IDE,可能会有一个困惑:写代码已经有 AI 帮忙了,还需要 Hermes Agent 做什么?这一章将给出答案。Hermes Agent 不是另一个写代码的 AI,而是一个常驻在团队里的"AI 协作成员",专门负责写代码之外的协作环节——讨论需求、审查代码、查日志、监控告警、生成报告。学完本章,读者将清楚 Hermes 的定位以及它在团队里的角色。

亢少军的博客 73

把时间当成数据:SAP ABAP 里的 DATS/TIMS 与 DATN/TIMN,怎么选才不踩坑

SAP项目中日期时间字段的陷阱与优化 日期时间字段(DATS/TIMS)在SAP项目中看似简单却常引发事故,主要问题包括: 非法日期(如20201331)入库后,在月结、对账时爆发异常 空值被处理为00000000导致排序错误 ABAP提供两种处理方案: 经典类型DATS/TIMS:存储为字符格式,校验宽松易存脏数据 HANA原生类型DATN/TIMN:数据库级校验,写入即拦截非法值 优化建议: 在接口层和入库前进行严格校验 新项目优先使用DATN/TIMN类型 对遗留数据使用DATS_IS_VALID等函

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 180

《从零开始学习深度学习》项目教程

《从零开始学习深度学习》项目教程 1. 项目介绍 本项目是基于开源书籍《从零开始学习深度学习》的第三版,该书由Hanbit Media出版。项目旨在通过逐步构建一个名为DeZero的深度学习框架,帮助读者深入理解深度学习原理及其背后的技术。DeZero框架采用了动态计算图(Define-by-Run)的方式,这是现代深度学习框架如PyTorch和TensorFlow 2.0所采用的技术。通过本项目...

gitblog_00088的博客 732

【Frida Android】实战篇8:客户端证书认证绕过 Hook 教程

通过重写类的方法,自定义证书校验逻辑;利用配置文件,从系统层面管理证书信任策略;以及通过 OkHttp 框架提供的方法,实现证书的强绑定校验。这些内容主要围绕客户端如何验证服务端证书的合法性展开。客户端证书认证。当 APK 预置了客户端证书,并且服务端会对客户端证书进行校验时,传统的中间人攻击方式将面临新的挑战。我们将深入分析这种场景的工作原理,并详细介绍如何通过技术手段获取客户端证书、配置中间人工具,最终成功实现抓包分析。

水滴石穿 1425

零基础深度学习:用Python实现深度学习框架

本文介绍了如何从零开始用Python实现一个简单的深度学习框架。通过构建张量(Tensor)、自动微分、神经网络层(Dense)、优化器(SGD)等核心组件,帮助初学者理解深度学习的基本原理。文章详细展示了如何实现前向传播、反向传播和参数更新,并提供了完整的代码示例训练一个简单的回归任务。这种从底层实现的方式有助于掌握深度学习的核心概念,为后续学习TensorFlow/PyTorch等框架打下基础。

2501_91624122的博客 1182

使用Simulink对氢燃料电池与锂电池的混合储能系统进行建模

本文介绍了基于Simulink的氢燃料电池与锂电池混合储能系统建模与协同控制方法。系统通过氢燃料电池提供稳定能源,锂电池实现快速响应,结合DC-DC变换器和负载管理器实现动态调控。详细阐述了从模型创建、关键模块配置到控制器设计的完整流程,重点说明了功率分配算法和充放电控制策略的实现。通过仿真分析验证了系统在不同负载条件下的性能表现,包括响应速度、工作效率和稳定性等指标。该方案为新能源混合储能系统的设计与优化提供了实用指导,有助于提升系统可靠性和延长电池寿命。

amy_mhd的博客 314

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

DL with python(7)——TensorFlow实现自制mnist数据集

很长一段时间内,如何将调用自带数据集(如mnist、fashion)进行训练的神经网络算法,修改为调用自己数据进行学习的代码,是我作为一个非计算机专业人士的难题。 在使用matlab编写神经网络代码时,可以通过简单(与其他语言相比)的文件导入和函数调用,实现数据集的自制。但是python在这方面的难度就大了一些,没有师傅领进门的小白感到无能为力。 ...

qq_36108664的博客 2682

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

深度学习从零开始 3 项目教程

`deep-learning-from-scratch-3` 是一个由 O'Reilly Japan 提供的开源项目,旨在通过从头开始构建一个名为 "DeZero" 的深度学习框架,帮助读者深入理解现代深度学习框架的核心概念和实现细节。该项目是《ゼロから作る Deep Learning ❸》(O'Reilly Japan, 2020)一书的配套代码库。 DeZero 是一个小巧但功能强大的深度学

gitblog_00869的博客 900

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

使用numpy搭建自己的深度学习框架(零)

使用numpy搭建自己的深度学习框架简介第一个目标第二个目标第三个目标总结一下 注:本系列搭建的深度学习框架名称叫numpyflow,缩写nf,用以熟悉目前主流的深度学习框架的基础和原理。 简介 本系列主要记录自己仅仅使用numpy来搭建自己的toy级别的深度学习框架。麻雀虽小,五脏俱全。在搭建自己的框架之前,我们要搞清楚我们需要准备些什么。 我们首先要知道,深度学习是啥、为啥叫深度学习。对于初学...

艾与代码 1506

【赠书活动第3期】《PyTorch 2.0深度学习从零开始学》

PyTorch是一个开源的机器学习框架,它提供了动态计算图的支持,让用户能够自定义和训练自己的神经网络,目前是机器学习领域中流行的框架之一。本书基于PyTorch 2.0,详细介绍深度学习的基本理论、算法和应用案例,配套示例源代码、PPT课件。

夏天又到了的专栏 1754

如何快速安装和配置FairSeq:从零开始的深度学习框架搭建教程

FairSeq是Facebook AI研究团队开发的高性能序列到序列(Seq2Seq)学习框架,主要用于机器翻译、文本生成以及其他自然语言处理任务的研究与开发。本文将为您提供完整的FairSeq安装和配置指南,帮助您快速搭建这个强大的深度学习框架。🚀 ## 前置要求与系统准备 在开始安装FairSeq之前,请确保您的系统满足以下基本要求: - **Python版本**:Python >=

gitblog_00321的博客 822

飞桨PaddlePaddle深度学习框架:从零开始的完整安装指南

深度学习已成为现代人工智能的核心技术,而选择一个合适的开发框架至关重要。飞桨PaddlePaddle作为国内首个自主研发的深度学习平台,为开发者提供了强大的工具支持。本教程将带你一步步完成PaddlePaddle的安装配置,让你快速开启深度学习之旅。 ## 🎯 为什么选择飞桨PaddlePaddle? 飞桨PaddlePaddle不仅支持传统的深度学习任务,还提供了丰富的预训练模型和端到端开

gitblog_00957的博客 1154
上一篇: 神经网络理论入门
下一篇: 非等间隔采样时间序列预测的图模型
编码雪人
博客等级 码龄7年 177粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值