亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

實(shí)現(xiàn)屬于自己的TensorFlow(一) - 計(jì)算圖與前向傳播

Faremax / 865人閱讀

摘要:目前實(shí)現(xiàn)了前向傳播和反向傳播以及梯度下降優(yōu)化器,并寫了個(gè)優(yōu)化線性模型的例子。下篇中將繼續(xù)總結(jié)計(jì)算圖節(jié)點(diǎn)計(jì)算梯度的方法以及反向傳播和梯度下降優(yōu)化器的實(shí)現(xiàn)。

前段時(shí)間因?yàn)檎n題需要使用了一段時(shí)間TensorFlow,感覺這種框架很有意思,除了可以搭建復(fù)雜的神經(jīng)網(wǎng)絡(luò),也可以優(yōu)化其他自己需要的計(jì)算模型,所以一直想自己學(xué)習(xí)一下寫一個(gè)類似的圖計(jì)算框架。前幾天組會(huì)開完決定著手實(shí)現(xiàn)一個(gè)模仿TensorFlow接口的簡(jiǎn)陋版本圖計(jì)算框架以學(xué)習(xí)計(jì)算圖程序的編寫以及前向傳播和反向傳播的實(shí)現(xiàn)。目前實(shí)現(xiàn)了前向傳播和反向傳播以及梯度下降優(yōu)化器,并寫了個(gè)優(yōu)化線性模型的例子。

代碼放在了GitHub上,取名SimpleFlow, 倉庫鏈接: https://github.com/PytLab/sim...

雖然前向傳播反向傳播這些原理了解起來并不是很復(fù)雜,但是真正著手寫起來才發(fā)現(xiàn),里面還是有很多細(xì)節(jié)需要學(xué)習(xí)和處理才能對(duì)實(shí)際的模型進(jìn)行優(yōu)化(例如Loss函數(shù)對(duì)每個(gè)計(jì)算節(jié)點(diǎn)矩陣求導(dǎo)的處理)。其中SimpleFlow的代碼并沒有考慮太多的東西比如dtype和張量size的檢查等,因?yàn)橹皇菫榱藢?shí)現(xiàn)主要圖計(jì)算功能并沒有考慮任何的優(yōu)化, 內(nèi)部張量運(yùn)算使用的Numpy的接口(畢竟是學(xué)習(xí)和練手的目的嘛)。好久時(shí)間沒更新博客了,在接下來的幾篇里面我將把實(shí)現(xiàn)的過程的細(xì)節(jié)總結(jié)一下,希望可以給后面學(xué)習(xí)的童鞋做個(gè)參考。

正文

本文主要介紹計(jì)算圖以及前向傳播的實(shí)現(xiàn), 主要涉及圖的構(gòu)建以及通過對(duì)構(gòu)建好的圖進(jìn)行后序遍歷然后進(jìn)行前向傳播計(jì)算得到具體節(jié)點(diǎn)上的輸出值。

先貼上一個(gè)簡(jiǎn)單的實(shí)現(xiàn)效果吧:

import simpleflow as sf

# Create a graph
with sf.Graph().as_default():
    a = sf.constant(1.0, name="a")
    b = sf.constant(2.0, name="b")
    result = sf.add(a, b, name="result")

    # Create a session to compute
    with tf.Session() as sess:
        print(sess.run(result))
計(jì)算圖(Computational Graph)

計(jì)算圖是計(jì)算代數(shù)中的一個(gè)基礎(chǔ)處理方法,我們可以通過一個(gè)有向圖來表示一個(gè)給定的數(shù)學(xué)表達(dá)式,并可以根據(jù)圖的特點(diǎn)快速方便對(duì)表達(dá)式中的變量進(jìn)行求導(dǎo)。而神經(jīng)網(wǎng)絡(luò)的本質(zhì)就是一個(gè)多層復(fù)合函數(shù), 因此也可以通過一個(gè)圖來表示其表達(dá)式。

本部分主要總結(jié)計(jì)算圖的實(shí)現(xiàn),在計(jì)算圖這個(gè)有向圖中,每個(gè)節(jié)點(diǎn)代表著一種特定的運(yùn)算例如求和,乘積,向量乘積,平方等等... 例如求和表達(dá)式$f(x, y) = x + y$使用有向圖表示為:

表達(dá)式$f(x, y, z) = z(x+y)$使用有向圖表示為:

與TensorFlow的實(shí)現(xiàn)不同,為了簡(jiǎn)化,在SimpleFlow中我并沒有定義Tensor類來表示計(jì)算圖中節(jié)點(diǎn)之間的數(shù)據(jù)流動(dòng),而是直接定義節(jié)點(diǎn)的類型,其中主要定義了四種類型來表示圖中的節(jié)點(diǎn):

Operation: 操作節(jié)點(diǎn)主要接受一個(gè)或者兩個(gè)輸入節(jié)點(diǎn)然后進(jìn)行簡(jiǎn)單的操作運(yùn)算,例如上圖中的加法操作和乘法操作等。

Variable: 沒有輸入節(jié)點(diǎn)的節(jié)點(diǎn),此節(jié)點(diǎn)包含的數(shù)據(jù)在運(yùn)算過程中是可以變化的。

Constant: 類似Variable節(jié)點(diǎn),也沒有輸入節(jié)點(diǎn),此節(jié)點(diǎn)中的數(shù)據(jù)在圖的運(yùn)算過程中不會(huì)發(fā)生變化

Placeholder: 同樣沒有輸入節(jié)點(diǎn),此節(jié)點(diǎn)的數(shù)據(jù)是通過圖建立好以后通過用戶傳入的

其實(shí)圖中的所有節(jié)點(diǎn)都可以看成是某種操作,其中Variable, Constant, Placeholder都是一種特殊的操作,只是相對(duì)于普通的Operation而言,他們沒有輸入,但是都會(huì)有輸出(像上圖中的$x$, $y$節(jié)點(diǎn),他們本身輸出自身的值到$+$節(jié)點(diǎn)中去),通常會(huì)輸出到Operation節(jié)點(diǎn),進(jìn)行進(jìn)一步的計(jì)算。

下面我們主要介紹如何實(shí)現(xiàn)計(jì)算圖的基本組件: 節(jié)點(diǎn)和邊。

Operation節(jié)點(diǎn)

節(jié)點(diǎn)表示操作,邊代表節(jié)點(diǎn)接收和輸出的數(shù)據(jù),操作節(jié)點(diǎn)需要含有以下屬性:

input_nodes: 輸入節(jié)點(diǎn),里面存放與當(dāng)前節(jié)點(diǎn)相連接的輸入節(jié)點(diǎn)的引用

output_nodes: 輸出節(jié)點(diǎn), 存放以當(dāng)前節(jié)點(diǎn)作為輸入的節(jié)點(diǎn),也就是當(dāng)前節(jié)點(diǎn)的去向

output_value: 存儲(chǔ)當(dāng)前節(jié)點(diǎn)的數(shù)值, 如果是Add節(jié)點(diǎn),此變量就存儲(chǔ)兩個(gè)輸入節(jié)點(diǎn)output_value的和

name: 當(dāng)前節(jié)點(diǎn)的名稱

graph: 此節(jié)點(diǎn)所屬的圖

下面我們定義了Operation基類用于表示圖中的操作節(jié)點(diǎn)(詳見https://github.com/PytLab/sim...:

class Operation(object):
    """ Base class for all operations in simpleflow.

    An operation is a node in computational graph receiving zero or more nodes
    as input and produce zero or more nodes as output. Vertices could be an
    operation, variable or placeholder.
    """
    def __init__(self, *input_nodes, name=None):
        """ Operation constructor.

        :param input_nodes: Input nodes for the operation node.
        :type input_nodes: Objects of `Operation`, `Variable` or `Placeholder`.

        :param name: The operation name.
        :type name: str.
        """
        # Nodes received by this operation.
        self.input_nodes = input_nodes

        # Nodes that receive this operation node as input.
        self.output_nodes = []

        # Output value of this operation in session execution.
        self.output_value = None

        # Operation name.
        self.name = name

        # Graph the operation belongs to.
        self.graph = DEFAULT_GRAPH

        # Add this operation node to destination lists in its input nodes.
        for node in input_nodes:
            node.output_nodes.append(self)

        # Add this operation to default graph.
        self.graph.operations.append(self)

    def compute_output(self):
        """ Compute and return the output value of the operation.
        """
        raise NotImplementedError

    def compute_gradient(self, grad=None):
        """ Compute and return the gradient of the operation wrt inputs.
        """
        raise NotImplementedError

在初始化方法中除了定義上面提到的屬性外,還需要進(jìn)行兩個(gè)操作:

將當(dāng)前節(jié)點(diǎn)的引用添加到他輸入節(jié)點(diǎn)的output_nodes這樣可以在輸入節(jié)點(diǎn)中找到當(dāng)前節(jié)點(diǎn)。

將當(dāng)前節(jié)點(diǎn)的引用添加到圖中,方便后面對(duì)圖中的資源進(jìn)行回收等操作

另外,每個(gè)操作節(jié)點(diǎn)還有兩個(gè)必須的方法: comput_outputcompute_gradient. 他們分別負(fù)責(zé)根據(jù)輸入節(jié)點(diǎn)的值計(jì)算當(dāng)前節(jié)點(diǎn)的輸出值和根據(jù)操作屬性和當(dāng)前節(jié)點(diǎn)的值計(jì)算梯度。關(guān)于梯度的計(jì)算將在后續(xù)的文章中詳細(xì)介紹,本文只對(duì)節(jié)點(diǎn)輸出值的計(jì)算進(jìn)行介紹。

下面我以求和操作為例來說明具體操作節(jié)點(diǎn)的實(shí)現(xiàn):

class Add(Operation):
    """ An addition operation.
    """
    def __init__(self, x, y, name=None):
        """ Addition constructor.

        :param x: The first input node.
        :type x: Object of `Operation`, `Variable` or `Placeholder`.

        :param y: The second input node.
        :type y: Object of `Operation`, `Variable` or `Placeholder`.

        :param name: The operation name.
        :type name: str.
        """
        super(self.__class__, self).__init__(x, y, name=name)

    def compute_output(self):
        """ Compute and return the value of addition operation.
        """
        x, y = self.input_nodes
        self.output_value = np.add(x.output_value, y.output_value)
        return self.output_value

可見,計(jì)算當(dāng)前節(jié)點(diǎn)output_value的值的前提條件就是他的輸入節(jié)點(diǎn)的值在此之前已經(jīng)計(jì)算得到了。

Variable節(jié)點(diǎn)

Operation節(jié)點(diǎn)類似,Variable節(jié)點(diǎn)也需要output_value, output_nodes等屬性,但是它沒有輸入節(jié)點(diǎn),也就沒有input_nodes屬性了,而是需要在創(chuàng)建的時(shí)候確定一個(gè)初始值initial_value:

class Variable(object):
    """ Variable node in computational graph.
    """
    def __init__(self, initial_value=None, name=None, trainable=True): 
        """ Variable constructor.

        :param initial_value: The initial value of the variable.
        :type initial_value: number or a ndarray.

        :param name: Name of the variable.
        :type name: str.
        """
        # Variable initial value.
        self.initial_value = initial_value

        # Output value of this operation in session execution.
        self.output_value = None

        # Nodes that receive this variable node as input.
        self.output_nodes = []

        # Variable name.
        self.name = name

        # Graph the variable belongs to.
        self.graph = DEFAULT_GRAPH

        # Add to the currently active default graph.
        self.graph.variables.append(self)
        if trainable:
            self.graph.trainable_variables.append(self)

    def compute_output(self):
        """ Compute and return the variable value.
        """
        if self.output_value is None:
            self.output_value = self.initial_value
        return self.output_value
Constant節(jié)點(diǎn)和Placeholder節(jié)點(diǎn)

ConstantPlaceholder節(jié)點(diǎn)與Variable節(jié)點(diǎn)類似,具體實(shí)現(xiàn)詳見: https://github.com/PytLab/sim...

計(jì)算圖對(duì)象

在定義了圖中的節(jié)點(diǎn)后我們需要將定義好的節(jié)點(diǎn)放入到一個(gè)圖中統(tǒng)一保管,因此就需要定義一個(gè)Graph類來存放創(chuàng)建的節(jié)點(diǎn),方便統(tǒng)一操作圖中節(jié)點(diǎn)的資源。

class Graph(object):
    """ Graph containing all computing nodes.
    """
    def __init__(self):
        """ Graph constructor.
        """
        self.operations, self.constants, self.placeholders = [], [], []
        self.variables, self.trainable_variables = [], []

為了提供一個(gè)默認(rèn)的圖,在導(dǎo)入simpleflow模塊的時(shí)候創(chuàng)建一個(gè)全局變量來引用默認(rèn)的圖:

from .graph import Graph

# Create a default graph.
import builtins
DEFAULT_GRAPH = builtins.DEFAULT_GRAPH = Graph()

為了模仿TensorFlow的接口,我們給Graph添加上下文管理器協(xié)議方法使其成為一個(gè)上下文管理器, 同時(shí)也添加一個(gè)as_default方法:

class Graph(object):
    #...

    def __enter__(self):
        """ Reset default graph.
        """
        global DEFAULT_GRAPH
        self.old_graph = DEFAULT_GRAPH
        DEFAULT_GRAPH = self
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        """ Recover default graph.
        """
        global DEFAULT_GRAPH
        DEFAULT_GRAPH = self.old_graph

    def as_default(self):
        """ Set this graph as global default graph.
        """
        return self

這樣在進(jìn)入with代碼塊之前先保存舊的默認(rèn)圖對(duì)象然后將當(dāng)前圖賦值給全局圖對(duì)象,這樣with代碼塊中的節(jié)點(diǎn)默認(rèn)會(huì)添加到當(dāng)前的圖中。最后退出with代碼塊時(shí)再對(duì)圖進(jìn)行恢復(fù)即可。這樣我們可以按照TensorFlow的方式來在某個(gè)圖中創(chuàng)建節(jié)點(diǎn).

Ok,根據(jù)上面的實(shí)現(xiàn)我們已經(jīng)可以創(chuàng)建一個(gè)計(jì)算圖了:

import simpleflow as sf

with sf.Graph().as_default():
    a = sf.constant([1.0, 2.0], name="a")
    b = sf.constant(2.0, name="b")
    c = a * b
前向傳播(Feedforward)

實(shí)現(xiàn)了計(jì)算圖和圖中的節(jié)點(diǎn),我們需要對(duì)計(jì)算圖進(jìn)行計(jì)算, 本部分對(duì)計(jì)算圖的前向傳播的實(shí)現(xiàn)進(jìn)行總結(jié)。

會(huì)話

首先,我們需要實(shí)現(xiàn)一個(gè)Session來對(duì)一個(gè)已經(jīng)創(chuàng)建好的計(jì)算圖進(jìn)行計(jì)算,因?yàn)楫?dāng)我們創(chuàng)建我們之前定義的節(jié)點(diǎn)的時(shí)候其實(shí)只是創(chuàng)建了一個(gè)空節(jié)點(diǎn),節(jié)點(diǎn)中并沒有數(shù)值可以用來計(jì)算,也就是output_value是空的。為了模仿TensorFlow的接口,我們?cè)谶@里也把session定義成一個(gè)上下文管理器:

class Session(object):
    """ A session to compute a particular graph.
    """
    def __init__(self):
        """ Session constructor.
        """
        # Graph the session computes for.
        self.graph = DEFAULT_GRAPH

    def __enter__(self):
        """ Context management protocal method called before `with-block`.
        """
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        """ Context management protocal method called after `with-block`.
        """
        self.close()

    def close(self):
        """ Free all output values in nodes.
        """
        all_nodes = (self.graph.constants + self.graph.variables +
                     self.graph.placeholders + self.graph.operations +
                     self.graph.trainable_variables)
        for node in all_nodes:
            node.output_value = None

    def run(self, operation, feed_dict=None):
        """ Compute the output of an operation."""
        # ...
計(jì)算某個(gè)節(jié)點(diǎn)的輸出值

上面我們已經(jīng)可以構(gòu)建出一個(gè)計(jì)算圖了,計(jì)算圖中的每個(gè)節(jié)點(diǎn)與其相鄰的節(jié)點(diǎn)有方向的聯(lián)系起來,現(xiàn)在我們需要根據(jù)圖中節(jié)點(diǎn)的關(guān)系來推算出某個(gè)節(jié)點(diǎn)的值。那么如何計(jì)算呢? 還是以我們剛才$f(x, y, z) = z(x + y)$的計(jì)算圖為例,

若我們需要計(jì)算橙色$ imes$運(yùn)算節(jié)點(diǎn)的輸出值,我們需要計(jì)算與它相連的兩個(gè)輸入節(jié)點(diǎn)的輸出值,進(jìn)而需要計(jì)算綠色$+$的輸入節(jié)點(diǎn)的輸出值。我們可以通過后序遍歷來獲取計(jì)算一個(gè)節(jié)點(diǎn)所需的所有節(jié)點(diǎn)的輸出值。為了方便實(shí)現(xiàn),后序遍歷我直接使用了遞歸的方式來實(shí)現(xiàn):

def _get_prerequisite(operation):
    """ Perform a post-order traversal to get a list of nodes to be computed in order.
    """
    postorder_nodes = []

    # Collection nodes recursively.
    def postorder_traverse(operation):
        if isinstance(operation, Operation):
            for input_node in operation.input_nodes:
                postorder_traverse(input_node)
        postorder_nodes.append(operation)

    postorder_traverse(operation)

    return postorder_nodes

通過此函數(shù)我們可以獲取計(jì)算一個(gè)節(jié)點(diǎn)值所需要所有節(jié)點(diǎn)列表,再依次計(jì)算列表中節(jié)點(diǎn)的輸出值,最后便可以輕易的計(jì)算出當(dāng)前節(jié)點(diǎn)的輸出值了。

class Session(object):
    # ...
    def run(self, operation, feed_dict=None):
        """ Compute the output of an operation.

        :param operation: A specific operation to be computed.
        :type operation: object of `Operation`, `Variable` or `Placeholder`.

        :param feed_dict: A mapping between placeholder and its actual value for the session.
        :type feed_dict: dict.
        """
        # Get all prerequisite nodes using postorder traversal.
        postorder_nodes = _get_prerequisite(operation)

        for node in postorder_nodes:
            if type(node) is Placeholder:
                node.output_value = feed_dict[node]
            else:  # Operation and variable
                node.compute_output()

        return operation.output_value
例子

上面我們實(shí)現(xiàn)了計(jì)算圖以及前向傳播,我們就可以創(chuàng)建計(jì)算圖計(jì)算表達(dá)式的值了, 如下:

$$ f = left[ egin{matrix} 1 & 2 & 3 3 & 4 & 5 end{matrix} ight] imes left[ egin{matrix} 9 & 8 7 & 6 10 & 11 end{matrix} ight] + 3 = left[ egin{matrix} 54 & 54 106 & 104 end{matrix} ight] $$

import simpleflow as sf

# Create a graph
with sf.Graph().as_default():
    w = sf.constant([[1, 2, 3], [3, 4, 5]], name="w")
    x = sf.constant([[9, 8], [7, 6], [10, 11]], name="x")
    b = sf.constant(1.0, "b")
    result = sf.matmul(w, x) + b

    # Create a session to compute
    with sf.Session() as sess:
        print(sess.run(result))

輸出值:

array([[  54.,   54.],
       [ 106.,  104.]])
總結(jié)

本文使用Python實(shí)現(xiàn)了計(jì)算圖以及計(jì)算圖的前向傳播,并模仿TensorFlow的接口創(chuàng)建了Session以及Graph對(duì)象。下篇中將繼續(xù)總結(jié)計(jì)算圖節(jié)點(diǎn)計(jì)算梯度的方法以及反向傳播和梯度下降優(yōu)化器的實(shí)現(xiàn)。

最后再附上simpleflow項(xiàng)目的鏈接, 歡迎相互學(xué)習(xí)和交流: https://github.com/PytLab/sim...

參考

Deep Learning From Scratch

https://en.wikipedia.org/wiki...

https://zhuanlan.zhihu.com/p/...

http://blog.csdn.net/magic_an...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/19692.html

相關(guān)文章

  • 實(shí)現(xiàn)屬于自己TensorFlow() - 計(jì)算圖與前向傳播

    摘要:目前實(shí)現(xiàn)了前向傳播和反向傳播以及梯度下降優(yōu)化器,并寫了個(gè)優(yōu)化線性模型的例子。下篇中將繼續(xù)總結(jié)計(jì)算圖節(jié)點(diǎn)計(jì)算梯度的方法以及反向傳播和梯度下降優(yōu)化器的實(shí)現(xiàn)。 前段時(shí)間因?yàn)檎n題需要使用了一段時(shí)間TensorFlow,感覺這種框架很有意思,除了可以搭建復(fù)雜的神經(jīng)網(wǎng)絡(luò),也可以優(yōu)化其他自己需要的計(jì)算模型,所以一直想自己學(xué)習(xí)一下寫一個(gè)類似的圖計(jì)算框架。前幾天組會(huì)開完決定著手實(shí)現(xiàn)一個(gè)模仿TensorF...

    davidac 評(píng)論0 收藏0
  • 從DensNet到CliqueNet,解讀北大在卷積架構(gòu)上探索

    摘要:首先第一種當(dāng)然是在年提出的,它奠定了整個(gè)卷積神經(jīng)網(wǎng)絡(luò)的基礎(chǔ)。其中局部感受野表示卷積核只關(guān)注圖像的局部特征,而權(quán)重共享表示一個(gè)卷積核在整張圖像上都使用相同的權(quán)值,最后的子采樣即我們常用的池化操作,它可以精煉抽取的特征。 近日,微軟亞洲研究院主辦了一場(chǎng)關(guān)于 CVPR 2018 中國論文分享會(huì),機(jī)器之心在分享會(huì)中發(fā)現(xiàn)了一篇非常有意思的論文,它介紹了一種新型卷積網(wǎng)絡(luò)架構(gòu),并且相比于 DenseNet...

    Jackwoo 評(píng)論0 收藏0
  • 從零開始PyTorch項(xiàng)目:YOLO v3目標(biāo)檢測(cè)實(shí)現(xiàn)

    摘要:全卷積神經(jīng)網(wǎng)絡(luò)僅使用卷積層,這就使其成為全卷積神經(jīng)網(wǎng)絡(luò)。輸入圖像中包含了真值對(duì)象框中心的網(wǎng)格會(huì)作為負(fù)責(zé)預(yù)測(cè)對(duì)象的單元格。在圖像中,它是被標(biāo)記為紅色的單元格,其中包含了真值框的中心被標(biāo)記為黃色。在過去幾個(gè)月中,我一直在實(shí)驗(yàn)室中研究提升目標(biāo)檢測(cè)的方法。在這之中我獲得的較大啟發(fā)就是意識(shí)到:學(xué)習(xí)目標(biāo)檢測(cè)的較佳方法就是自己動(dòng)手實(shí)現(xiàn)這些算法,而這正是本教程引導(dǎo)你去做的。 在本教程中,我們將使用 P...

    sourcenode 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<