оригинал:Deep Learning From Scratch III: Training Criterion - deep ideas
Перевод: Сунь Имэн
- Глава 1: Вычислительные графики
- Глава 2: Персептроны
- Глава 3: Стандарты обучения
- Глава 4: Градиентный спуск и обратное распространение
- Глава 5: Многослойные персептроны
- Глава 6: TensorFlow
Training criterion
Теперь мы научились использовать линейные классификаторы для классификации точек и для вычисления вероятности того, что точка принадлежит определенному классу, если мы задаем матрицу весови предвзятостьподходящее значение.
Тогда, естественно, у нас возникает проблема: как задать весовую матрицуи предвзятостьподходящее значение. В красно-синем примере мы просто посмотрели на обучающие данные и угадали линию, которая точно разделяет точку на плоскости на две части.
Но в целом мы не хотим находить разделительную линию вручную, мы хотим, чтобы после ввода обучающих точек в компьютер, он сам находил хорошую разделительную линию.
Так как же нам оценить, хороша или плоха разделительная линия?
Скорость ошибочной классификации
В идеале мы хотим найти разделительную линию с как можно меньшим количеством ошибок. Мы не знаем истинного распределения генерации данныхподобен, но для каждой точки x в нем и каждого класса c(x), которому он принадлежит, мы хотим минимизировать вероятность того, что персептрон неправильно классифицирует. то есть свести к минимуму错分率
:
Часто мы не знаем истинного распределения генерации данных, поэтому невозможно рассчитать точное значение частоты ошибок. Но у нас есть набор обучающих точек, содержащих значение x и категорию, к которой он принадлежит.
В дальнейшем будем использовать формуМатрица представляет собой набор обучающих точек, каждая строка представляет обучающую точку, количество столбцов матрицы равно размерности обучающих точек, а каждый столбец представляет размерность.
Кроме того, мы используем матрицу истинной правильной классификацииозначает, что еслиобучающие выборки имеют классификацию,Так[math]$ c_{i,j} = 1 $[/math]
. Точно так же мы также используем матрицупредставляет собой ожидаемую классификацию, если перваяПрогнозируемые классы обучающих выборок:,Так.
Наконец, мы используем матрицудля представления вероятности, гдеозначает первыйобучающие выборки относятся к категорииВероятность.
С данными обучения мы можем найти классификатор с наименьшей частотой ошибочной классификации выборки:
Однако мы обнаружили, что найти линейный классификатор с наименьшей частотой ошибок классификации очень сложно: по мере увеличения размерности входных данных вычислительная сложность растет экспоненциально, что делает поиск этого линейного классификатора нецелесообразным.
Даже если мы найдем классификатор с наименьшим уровнем ошибочной классификации, он может стать более устойчивым к неизвестным данным, разделив классы дальше друг от друга, даже если при этом он не уменьшит частоту ошибочной классификации выборки.
оценка максимального правдоподобия
Альтернативаоценка максимального правдоподобия, при оценке максимального правдоподобия мы пытаемся найти конкретные значения параметров, которые делают вероятностьмаксимизировать.
Здесь мы представляем
называетсяперекрестная потеря энтропии. Здесь мы желаемминимизировать.
мы кладемсчитается однимOperation
, то его ввод: данные, реальная классификацияи ожидаемая вероятность.то есть операцияВывод.
Это позволяет рассчитать истинное количество потерь:
построить один для вычисленийизoperation
построить для расчетаизoperaion
, мы можем поставить несколько первичныхoperation
Комбинированный. Во-первых,Можно использовать матричные операции.выражается следующим образом:
Отсюда мы можем найти, что упомянутый выше «первичный»operation
Есть следующие:
-
log: операция логарифмирования (логарифма), выполняемая над каждым элементом матрицы или вектора.
-
⊙: Умножение каждого элемента между двумя матрицами
-
: сумма всех столбцов матрицы
-
: сумма всех строк матрицы
-
−: Поэлементное отрицание
Затем реализуем их по отдельности.
1. log
Поэлементные логарифмические операции над тензорами:
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
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]
class log(Operation):
""" 对每一个元素进行对数运算
"""
def __init__(self, x):
""" 构造 log
参数列表:
x: 输入节点
"""
super().__init__([x])
def compute(self, x_value):
""" 计算对数 operation 的输出
参数列表:
x_value: 输入值
"""
return np.log(x_value)
2. Умножение / ⊙
Поэлементное умножение двух тензоров одинаковой формы
class multiply(Operation):
""" 对每一个元素,返回 x * y 的值
"""
def __init__(self, x, y):
""" 构造乘法
参数列表:
x: 第一个乘数的输入节点
y: 第二个乘数的输入节点
"""
super().__init__([x, y])
def compute(self, x_value, y_value):
""" 计算乘法 operation 的输出
Args:
x_value: 第一个乘数的值
y_value: 第二个乘数的值
"""
return x_value * y_value
3. reduce_sum
Чтобы вычислить несколько сумм за одну операцию (например, сумму всех строк, сумму всех столбцов и т. д.), мы указываем ось значений, если ось = 0 означает вычисление суммы строки, ось = 1 означает вычислить сумму столбца, И т.д., и т.п. Нампи делает именно это.
class reduce_sum(Operation):
""" 计算张量中元素延某一或某些维度的总和
"""
def __init__(self, A, axis=None):
""" 构造 reduce_sum
参数列表:
A: 要进行 reduce 运算的张量
axis: 需要 reduce 的维度,如果 `None`(即缺省值),则延所有维度 reduce
"""
super().__init__([A])
self.axis = axis
def compute(self, A_value):
""" 计算 reduce_sum operation 的输出值
参数列表:
A_value: 输入的张量值
"""
return np.sum(A_value, self.axis)
4. Отрицательная операция
Отмените каждый элемент тензора:
class negative(Operation):
""" 逐元素计算负数
"""
def __init__(self, x):
""" 构造负运算
参数列表:
x: 输入节点
"""
super().__init__([x])
def compute(self, x_value):
""" 计算负运算 operation 的输出
参数列表:
x_value: 输入值
"""
return -x_value
5. Объедините этиoperation
Используя эти операции, описанные выше, теперь мы можем записать операцию для J следующим образом:
J = negative(reduce_sum(reduce_sum(multiply(c, log(p)), axis=1)))
### Пример Теперь давайте рассчитаем потери красного/синего персептрона.
import numpy as np
red_points = np.random.randn(50, 2) - 2*np.ones((50, 2))
blue_points = np.random.randn(50, 2) + 2*np.ones((50, 2))
# 创建一个新的 graph
Graph().as_default()
X = placeholder()
c = placeholder()
W = Variable([
[1, -1],
[1, -1]
])
b = Variable([0, 0])
p = softmax(add(matmul(X, W), b))
# 交叉熵损失
J = negative(reduce_sum(reduce_sum(multiply(c, log(p)), axis=1)))
session = Session()
print(session.run(J, {
X: np.concatenate((blue_points, red_points)),
c:
[[1, 0]] * len(blue_points)
+ [[0, 1]] * len(red_points)
}))
Если у вас есть какие-либо вопросы, не стесняйтесь задавать их в комментариях.