Нативная нейронная сеть, часть II: персептрон

искусственный интеллект TensorFlow Python Нейронные сети
Нативная нейронная сеть, часть II: персептрон

оригинал:Deep Learning From Scratch II: Perceptrons - deep ideas

Перевод: Сунь Имэн



Персептроны

захватывающий пример

感知机Это небольшая форма нейронной сети, являющаяся базовым блоком более сложных структур.

Прежде чем углубляться в детали, давайте взглянем на этот захватывающий пример. Предположим, у нас есть набор данных из ста точек на плоскости, половина из которых — красные, а другая половина — синие.

Нажмите, чтобы запустить код и наблюдать за распределением баллов.

import numpy as np
import matplotlib.pyplot as plt[/amalthea_pre_exercise_code]
[amalthea_sample_code]
# 创建一些集中于 (-2, -2) 的红点
red_points = np.random.randn(50, 2) - 2*np.ones((50, 2))

# 创建一些集中于 (2, 2) 的蓝点
blue_points = np.random.randn(50, 2) + 2*np.ones((50, 2))

# 把红点和蓝点都在图上画出来
plt.scatter(red_points[:,0], red_points[:,1], color='red')
plt.scatter(blue_points[:,0], blue_points[:,1], color='blue')

Как показано на рисунке, красные точки сосредоточены в(-2, -2), а синие точки фокусируются на(2, 2). Глядя на данные, как вы думаете, есть ли способ определить, красная точка или синяя?

если вы спросите(3, 2)Какого она цвета, синий вы ответите сразу, даже если этой точки нет в приведенных выше данных, мы все равно можем судить о ее цвете по области (синей) в которой она находится.

Но есть ли более общий способ сделать вывод, что синий цвет более вероятен? Очевидно, мы можем провести линию на графике вышеy = -x, идеально делит пространство на красную и синюю зоны.

# 创建一些集中于 (-2, -2) 的红点
red_points = np.random.randn(50, 2) - 2*np.ones((50, 2))

# 创建一些集中于 (2, 2) 的蓝点
blue_points = np.random.randn(50, 2) + 2*np.ones((50, 2))

# 把红点和蓝点都在图上画出来
plt.scatter(red_points[:,0], red_points[:,1], color='red')
plt.scatter(blue_points[:,0], blue_points[:,1], color='blue')[/amalthea_pre_exercise_code]
[amalthea_sample_code]
# 画一条线 y = -x
x_axis = np.linspace(-4, 4, 100)
y_axis = -x_axis
plt.plot(x_axis, y_axis)

мы можем использовать权向量 wи偏置 bнеявно представлять линию, точку на линииxвстречаетw^T x + b = 0.

Подставьте данные в приведенном выше примере, чтобы получитьw = (1,1)^T , b = 0. следовательноw^T x + bравный(1,1)^T \cdot x.

Таким образом, эту строку можно выразить так:

(1,1)^T \cdot x  = 0

Ну, а теперь, чтобы судить, красное оно или синее, просто оцените, находится оно выше или ниже линии: поставьте точкуxзаменятьw^T x + b, по знаку результата, если он положительный,xЧуть выше линии, минус чуть ниже.

Как пункт выше(3,2):
\pmatrix{ 1 & 1 } \cdot \pmatrix{ 3 \cr 2 }  = 5
5 > 0, поэтому точка находится над линией, поэтому она синяя.


Определение персептрона

Часто分类器(classifier) функция:\hat{c}: R^d -> { 1, 2, … , C}, вы можете сопоставить точку с категорией (всего категорий C).

и один二元分类器То есть всего есть две категории (C = 2)из分类器.

Что мы используем, чтобы судить о красной точке и синей точке感知机, это二元分类器w \in R^dи偏置 b \in R^d:

\hat{c} (x) =\begin {cases} 1, & w^{T} x + b \geq 0 \\\ 2, & w^{T} x + b < 0 \end {cases}

это\hat{c} (x),будетR^dОн разделен на два пространства, каждое из которых соответствует категории.

Примерами красных и синих точек являются двумерные (размерd = 2), в двумерном случае пространство разделено по линии. способствоватьdВ случае размерности деление плоскости всегда происходит вдольd - 1размерная гиперплоскость.


От классификации категорий к расчету вероятностей

В практических приложениях мы не только хотим знать, к какому классу, скорее всего, относится точка, мы также задаемся вопросом, какова вероятность того, что точка принадлежит к определенному классу.

Прежде чем судить о красном и синем, подставим данные точки x, если получимw^T x + bЧем выше значение, тем дальше эта точка должна быть от разделительной линии, и тем больше мы уверены, что она синяя.

но когда мы получаемw^T x + bПри значении , мы не можем сказать, велико оно или нет. Затем, чтобы преобразовать это значение в вероятность, мы можем сжать значения, чтобы они были распределены между 0 и 1.

Это можно сделать сsigmoidФункция σ реализует:
p(\hat{c} (x) = 1 | x) = σ( w^T x + b)
в\sigma(a) = \frac{1}{1 + e^{-a}}

sigmoid 的计算图

ПосмотримsigmoidРеализация функции:

import matplotlib.pyplot as plt
import numpy as np
# 创建从 -5 到 5 的间隔,步长 0.01
a = np.arange(-5, 5, 0.01)

# 计算对应的 sigmoid 函数的值
s = 1 / (1 + np.exp(-a))

# 画出结果
plt.plot(a, s)
plt.grid(True)
plt.show()

Как показано, когдаw^T x + b = 0, то есть когда точка лежит на разделительной линии,sigmoidВероятность того, что функция получит это значение, соответствует 0,5. По мере приближения асимптоты к 1w^T x + bЧем больше значение , тем ближе асимптота к 0, темw^T x + bтем меньше значение.

Оправдать наши ожидания.

Теперь давайте определимsigmoidфункциональныйOperation,этоOperationМы будем использовать позже:


class Operation:
    """Represents a graph node that performs a computation.

    An `Operation` is a node in a `Graph` that takes zero or
    more objects as input, and produces zero or more objects
    as output.
    """

    def __init__(self, input_nodes=[]):
        """Construct Operation
        """
        self.input_nodes = input_nodes

        # Initialize list of consumers (i.e. nodes that receive this operation's output as input)
        self.consumers = []

        # Append this operation to the list of consumers of all input nodes
        for input_node in input_nodes:
            input_node.consumers.append(self)

        # Append this operation to the list of operations in the currently active default graph
        _default_graph.operations.append(self)

    def compute(self):
        """Computes the output of this operation.
        "" Must be implemented by the particular operation.
        """
        pass

class Graph:
    """Represents a computational graph
    """

    def __init__(self):
        """Construct Graph"""
        self.operations = []
        self.placeholders = []
        self.variables = []

    def as_default(self):
        global _default_graph
        _default_graph = self

class placeholder:
    """Represents a placeholder node that has to be provided with a value
       when computing the output of a computational graph
    """

    def __init__(self):
        """Construct placeholder
        """
        self.consumers = []

        # Append this placeholder to the list of placeholders in the currently active default graph
        _default_graph.placeholders.append(self)

class Variable:
    """Represents a variable (i.e. an intrinsic, changeable parameter of a computational graph).
    """

    def __init__(self, initial_value=None):
        """Construct Variable

        Args:
          initial_value: The initial value of this variable
        """
        self.value = initial_value
        self.consumers = []

        # Append this variable to the list of variables in the currently active default graph
        _default_graph.variables.append(self)

class add(Operation):
    """Returns x + y element-wise.
    """

    def __init__(self, x, y):
        """Construct add

        Args:
          x: First summand node
          y: Second summand node
        """
        super().__init__([x, y])

    def compute(self, x_value, y_value):
        """Compute the output of the add operation

        Args:
          x_value: First summand value
          y_value: Second summand value
        """
        return x_value + y_value

class matmul(Operation):
    """Multiplies matrix a by matrix b, producing a * b.
    """

    def __init__(self, a, b):
        """Construct matmul

        Args:
          a: First matrix
          b: Second matrix
        """
        super().__init__([a, b])

    def compute(self, a_value, b_value):
        """Compute the output of the matmul operation

        Args:
          a_value: First matrix value
          b_value: Second matrix value
        """
        return a_value.dot(b_value)
class Session:
    """Represents a particular execution of a computational graph.
    """

    def run(self, operation, feed_dict={}):
        """Computes the output of an operation

        Args:
          operation: The operation whose output we'd like to compute.
          feed_dict: A dictionary that maps placeholders to values for this session
        """

        # Perform a post-order traversal of the graph to bring the nodes into the right order
        nodes_postorder = traverse_postorder(operation)

        # Iterate all nodes to determine their value
        for node in nodes_postorder:

            if type(node) == placeholder:
                # Set the node value to the placeholder value from feed_dict
                node.output = feed_dict[node]
            elif type(node) == Variable:
                # Set the node value to the variable's value attribute
                node.output = node.value
            else:  # Operation
                # Get the input values for this operation from node_values
                node.inputs = [input_node.output for input_node in node.input_nodes]

                # Compute the output of this operation
                node.output = node.compute(*node.inputs)

            # Convert lists to numpy arrays
            if type(node.output) == list:
                node.output = np.array(node.output)

        # Return the requested node value
        return operation.output


def traverse_postorder(operation):
    """Performs a post-order traversal, returning a list of nodes
    in the order in which they have to be computed

    Args:
       operation: The operation to start traversal at
    """

    nodes_postorder = []

    def recurse(node):
        if isinstance(node, Operation):
            for input_node in node.input_nodes:
                recurse(input_node)
        nodes_postorder.append(node)

    recurse(operation)
    return nodes_postorder
[/amalthea_pre_exercise_code]
[amalthea_sample_code]
class sigmoid(Operation):
    """返回元素 x 的 sigmoid 结果。
    """

    def __init__(self, a):
        """构造 sigmoid

        参数列表:
          a: 输入节点
        """
        super().__init__([a])

    def compute(self, a_value):
        """计算本 sigmoid operation 的输出

        参数列表:
          a_value: 输入值
        """
        return 1 / (1 + np.exp(-a_value))
def reTrue():
    return True

reTrue()

1. Приведите пример

Теперь мы можем сделать Python感知机, чтобы решить предыдущую красно-синюю проблему. использовать это снова感知机считать(3,2)^Tэто вероятность синей точки

# 创建一个新 graph
Graph().as_default()

x = placeholder()
w = Variable([1, 1])
b = Variable(0)
p = sigmoid( add(matmul(w, x), b) )

session = Session()
print(session.run(p, {
    x: [3, 2]
}))

Мультиклассовый персептрон

До сих пор мы использовали только感知机сделал бинарный分类器, используется для расчета вероятности того, что точка принадлежит к определенной категории (всего две категории)p, то, естественно, вероятность принадлежности к другому классу равна1 - p.

Но часто на практике количество категорий будет больше двух. Например, при классификации изображений может быть много категорий для вывода (например, собаки, стулья, люди, дома и т. д.).

Поэтому мы будем感知机Расширьте его, чтобы поддерживать возможность экспорта нескольких категорий.

мы по-прежнему берем константуCкак количество категорий. Но больше не используйте предыдущее двоичное время权向量 w, но ввести权矩阵 W \in R^{d \times C}.

权矩阵Каждый столбец содержит отдельный线性分类器веса в , по одному для каждой категории分类器.

В двоичном формате мы должны вычислитьw^T xизскалярное произведение, и теперь мы хотим вычислитьxW. рассчитатьxWвозвращеноR^CВектор , его члены можно рассматривать как权矩阵Результат скалярного произведения разных столбцов.

Затем мы конвертируем векторxWплюс偏置向量 b \in R^C. векторbТовар соответствует категории.

Это создаетR^CВектор , каждый элемент этого вектора представляет, что точка принадлежит к определенной категории (всегоCкатегории) возможность.

Процесс может показаться сложным, но на самом деле это умножение матриц является просто параллельнымCкаждая из категорий, выполняет свои соответствующие线性分类器Вот и все, у каждого из них есть своя разделительная линия, и эту разделительную линию можно еще определить по заданному вектору весов и偏置Неявное представление, но здесь权向量Зависит от权矩阵Столбцы предоставляются, в то время как偏置являетсяbЭлементы вектора.

多类型感知机的计算图


1. Softmax

оригинальный感知机Сгенерируйте один скаляр, черезsigmoid, мы сжимаем этот скаляр, чтобы получить вероятность, распределенную между 0 и 1.

Продвижение в несколько категорий感知机, который генерирует векторa \in R^m. Точно так жеiЧем больше значение терма, тем больше мы уверены, что входная точка принадлежит первомуiкатегории.

Следовательно, мы также хотим преобразовать векторaПреобразованный в вектор вероятности, каждый элемент вектора представляет вероятность того, что входное значение принадлежит каждой категории, каждый элемент вектора распределен между 0 и 1, а сумма всех элементов равна 1.

Для этого обычно используютsoftmaxфункция.SoftmaxФункция на самом делеsigmoidОбобщение в случае многоклассового вывода:

[math] ? \sigma(a)_i = \frac{e^{a_i}}{\sum_{j=1}^C e^{a_j}} ? [/math]

class softmax(Operation):
    """返回 a 的 softmax 函数结果.
    """

    def __init__(self, a):
        """构造 softmax

        参数列表:
          a: 输入节点
        """
        super().__init__([a])

    def compute(self, a_value):
        """计算 softmax operation 的输出值

        参数列表:
          a_value: 输入值
        """
        return np.exp(a_value) / np.sum(np.exp(a_value), axis=1)[:, None]

2. Пакетный расчет

Мы можем передать сразу несколько значений в виде матрицы. То есть, раньше мы могли проходить только по одной точке за раз, теперь мы можем проходить по матрице за разX \in R^{N \times d}, каждая строка матрицы содержит точку (всегоNстрока, содержащаяNКусокdизмерение).

Мы называем эту матрицу пакетом.

В этом случае мы вычисляемXWвместоxW. рассчитатьXWвернетN \times Cматрица, каждая строка матрицы содержитxW.

Давайте добавим еще одну строку в каждую строку偏置向量 b(В настоящее времяbЯвляется1 \times mвектор строки).

Таким образом, весь процесс вычисляет функциюf : R^{N \times d} -> R^mf(X) = \sigma(XW + b). здесь计算图следующее:

批量计算图


3. Пример

Давайте обобщим предыдущий красно-синий пример для поддержки пакетных вычислений и многоклассового вывода.

# 创建一个新 graph
Graph().as_default()

X = placeholder()

# 为两种输出类别创建一个权矩阵:
# 蓝色的权向量是 (1, 1) ,红色是 (-1, -1) 
W = Variable([
    [1, -1],
    [1, -1]
])
b = Variable([0, 0])
p = softmax( add(matmul(X, W), b) )

# 创建一个 Session,针对我们的蓝色/红色点运行 perceptron
session = Session()
output_probabilities = session.run(p, {
    X: np.concatenate((blue_points, red_points))
})

# 打印前 10 行, 也就是前 10 个点的概率
print(output_probabilities[:10])

Поскольку все первые 10 точек в наборе данных окрашены в синий цвет, вывод персептрона, скорее всего, будет синим (левый столбец), а не красным.

Если у вас есть какие-либо вопросы, не стесняйтесь задавать их в разделе комментариев к оригинальному сообщению.