Да, это вызывает неопределенное поведение. Lambdas будет ссылаться на объекты, расположенные в стеке, которые вышли за рамки. (Технически, как я понимаю, поведение определяется до доступа lambdas a
и / или b
. Если вы никогда не вызываете возвращенные lambdas, тогда нет UB.)
Это неопределенное поведение так же, как неопределенное поведение, чтобы вернуть ссылку на локализованный стек, а затем использовать эту ссылку после локального выхода из области видимости, за исключением того, что в этом случае она немного запутывается lambda.
Кроме того, обратите внимание, что порядок, в котором вызывается lambdas, не указан - компилятор может вызывать f.second()
перед f.first()
, потому что оба являются частью одного и того же полного выражения. Поэтому, даже если мы исправим неопределенное поведение, вызванное использованием ссылок на уничтоженные объекты, как 2 0
, так и 2 1
являются еще действительными выходами из этой программы, и которые вы получаете, зависит от порядка, в котором ваш компилятор решает выполнить лямбды. Обратите внимание, что это не неопределенное поведение , потому что компилятор вообще не может делать вообще , а просто имеет некоторую свободу в решении порядка некоторые вещи .
(Имейте в виду, что <<
в вашей функции main()
вызывает пользовательскую функцию operator<<
, а порядок, в котором вычисляются аргументы функции, не указан. могут испускать код, который оценивает все аргументы функции в одном и том же полном выражении в любом порядке, с ограничением, что все аргументы функции должны быть оценены до того, как эта функция будет вызвана.)
Исправить первая проблема, используйте std::shared_ptr
для создания объекта с подсчетом ссылок. Захватите этот общий указатель по значению, и lambdas сохранит объект, указывающий на объект, до тех пор, пока они (и любые их копии) существуют. Этот выделенный кучей объект - это где мы будем хранить общее состояние a
и b
.
Чтобы исправить вторую проблему, оцените каждую лямбда в отдельном заявлении.
Вот ваш код, переписанный с неопределенным поведением, и с f.first()
гарантированно вызывается перед f.second()
:
std::pair, std::function> addSome() {
// We store the "a" and "b" ints instead in a shared_ptr containing a pair.
auto numbers = std::make_shared>(0, 0);
// a becomes numbers->first
// b becomes numbers->second
// And we capture the shared_ptr by value.
return std::make_pair(
[numbers] {
++numbers->first;
++numbers->second;
return numbers->first + numbers->second;
},
[numbers] {
return numbers->first;
}
);
}
int main() {
auto f = addSome();
// We break apart the output into two statements to guarantee that f.first()
// is evaluated prior to f.second().
std::cout << f.first();
std::cout << " " << f.second();
return 0;
}
Вы инициализируете переменную весов нулями, поэтому оптимизатор не может вычислить градиенты. Кроме того, 0.5
чрезвычайно высока для скорости обучения спуска градиента.
Попробуйте инициализировать переменную вашего веса случайным образом:
W = tf.get_variable("W", shape=[784, 10], initializer=tf.random_uniform_initializer())
b = tf.get_variable("b", shape=[10], initializer=tf.zeros_initializer())
И установите скорость обучения на что-то порядка 0.0005
.
Есть также другие инициализаторы , которые вы можете предпочесть вместо этого.
Учебное видео для Tensorboard охватывает точный сценарий инициализации нулевого веса. Вы уже пишете сводку в своем коде, это может быть хорошей идеей, чтобы просмотреть это видео, чтобы посмотреть, как его использовать, чтобы помочь отлаживать.