Python经常使用list这样的数据结构进行迭代操作,但也存在一些缺陷:当数据量比较大,内存很紧缺时,迭代过的数据依旧保存在内存中显然不是什么好的方案。另外,一些序列是递推无限的,无法使用list存储,比如tensorflow中每个batch图像数据的生成,都是使用生成器进行在线读取训练并释放内存的。因此,只能使用生成器(generator)。比较复杂的生成器的关键字是yield。yield意为停止和获取得到,在生成器中这两层意思都有。
简单生成器我们经常用,比如列表生成式
generator_ex = (x*x for x in range(10))
for i in generator_ex:
print(i)
for循环隐式调用next(generator_ex)返回生成器的计算值
list=[i**2 for i in range(10)]
以上常用列表生成式实际上是用"[]"将生成器转成一个list。
再看一个斐波那契数列的生成器示例:
def fib(max):
n,a,b =0,0,1
while n < max:
yield b
a,b =b,a+b
n = n+1
return 'done'
for i in fib(6):
print(i)
>>> for i in fib(6):
... print(i)
...
1
1
2
3
5
8
每次隐式调用next(fib),生成器函数运算到yield语句就获取它后面的值(返回)并停止指令保存当前的运行状态。下一次next时从yield的下一句执行。
再看一个使用生成器实现并发运算的例子:
import time
def consumer(name):
print("%s 准备学习啦!" %name)
while True:
lesson = yield
print("开始[%s]了,[%s]老师来讲课了!" %(lesson,name))
def producer():
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("同学们开始上课 了!")
for i in range(10):
time.sleep(1)
print("到了两个同学!")
c.send(i)
c2.send(i)
producer()
调用的执行结果为:
A 准备学习啦!
B 准备学习啦!
同学们开始上课 了!
到了两个同学!
开始[0]了,[A]老师来讲课了!
开始[0]了,[B]老师来讲课了!
到了两个同学!
开始[1]了,[A]老师来讲课了!
开始[1]了,[B]老师来讲课了!
到了两个同学!
开始[2]了,[A]老师来讲课了!
开始[2]了,[B]老师来讲课了!
到了两个同学!
开始[3]了,[A]老师来讲课了!
开始[3]了,[B]老师来讲课了!
到了两个同学!
开始[4]了,[A]老师来讲课了!
开始[4]了,[B]老师来讲课了!
到了两个同学!
开始[5]了,[A]老师来讲课了!
开始[5]了,[B]老师来讲课了!
到了两个同学!
开始[6]了,[A]老师来讲课了!
开始[6]了,[B]老师来讲课了!
到了两个同学!
开始[7]了,[A]老师来讲课了!
开始[7]了,[B]老师来讲课了!
到了两个同学!
开始[8]了,[A]老师来讲课了!
开始[8]了,[B]老师来讲课了!
到了两个同学!
开始[9]了,[A]老师来讲课了!
开始[9]了,[B]老师来讲课了!
这个还是被很常用的。
在看一个tensorflow里面每个epoch用的例子:
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
batch_size = 4
num_classes = 2
num_steps = 10
state_size = 4
learning_rate = 0.2
def gen_data(size=1000000):
"""
生成数据:
输入数据X:在时间t,Xt的值有50%的概率为1,50%的概率为0;
输出数据Y:在实践t,Yt的值有50%的概率为1,50%的概率为0,除此之外,如果`Xt-3 == 1`,Yt为1的概率增加50%, 如果`Xt-8 == 1`,则Yt为1的概率减少25%, 如果上述两个条件同时满足,则Yt为1的概率为75%。
"""
X = np.array(np.random.choice(2, size=(size,)))
Y = []
for i in range(size):
threhold = 0.5
if X[i - 3] == 1:
threhold += 0.5
if X[i - 8] == 1:
threhold -= 0.25
if np.random.rand() > threhold:
Y.append(0)
else:
Y.append(1)
return X, np.array(Y)
def gen_batch(raw_data, batch_size, num_steps):
raw_x, raw_y = raw_data
data_x = raw_x.reshape(-1, batch_size, num_steps) # 将一维向量数据转为三围张量
data_y = raw_y.reshape(-1, batch_size, num_steps)
for i in range(data_x.shape[0]):
print(data_x[i].shape, data_y[i].shape)
yield (data_x[i], data_y[i]) # yield返回数据并停止
def gen_epochs(n): #每个epoch使用一个batch的数据进行训练
'''这里的n就是训练过程中用的epoch,即在样本规模上循环的次数'''
for i in range(n):
yield (gen_batch(gen_data(), batch_size, num_steps))
x = tf.placeholder(tf.int32, [batch_size, num_steps], name='input_placeholder')
y = tf.placeholder(tf.int32, [batch_size, num_steps], name='output_placeholder')
init_state = tf.zeros([batch_size, state_size])
x_one_hot = tf.one_hot(x, num_classes)
"""
tf.unstack()
将给定的R维张量拆分成R-1维张量
将value根据axis分解成num个张量,返回的值是list类型,如果没有指定num则根据axis推断出!
"""
rnn_inputs = tf.unstack(x_one_hot, axis=1)
# tf.unstack将张量按照某个维度分解成低一个维度的张量
with tf.variable_scope("rnn_cell"):
W = tf.get_variable("W", [num_classes + state_size, state_size])
b = tf.get_variable("b", [state_size])
def rnn_cell(rnn_input, state):
with tf.variable_scope("rnn_cell", reuse=True):
W = tf.get_variable("W", [num_classes + state_size, state_size])
b = tf.get_variable("b", [state_size])
return tf.nn.tanh(tf.matmul(tf.concat((rnn_input, state), 1), W) + b)
state = init_state
rnn_outputs = []
for rnn_input in rnn_inputs:
state = rnn_cell(rnn_input, state)
rnn_outputs.append(state)
final_state = rnn_outputs[-1]
with tf.variable_scope("softmax"):
W = tf.get_variable("W", [state_size, num_classes])
b = tf.get_variable("b", [num_classes], initializer=tf.constant_initializer(0.0))
logits = [tf.matmul(rnn_output, W) + b for rnn_output in rnn_outputs]
predictions = [tf.nn.softmax(logit) for logit in logits]
y_as_list = tf.unstack(y, num=num_steps, axis=1)
loss = [tf.nn.sparse_softmax_cross_entropy_with_logits(labels=label, logits=logit)
for (logit, label) in zip(predictions, y_as_list)
]
total_loss = tf.reduce_mean(loss)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(total_loss)
def train_network(num_epochs, num_steps, state_size=4, verbose=True):
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
training_losses = []
for idx, epoch in enumerate(gen_epochs(num_epochs)):
training_loss = 0
training_state = np.zeros((batch_size, state_size))
if verbose:
print("\n EPOCH", idx)
for step, (X, Y) in enumerate(epoch):
tr_losses, training_loss_, training_state, _ = sess.run([loss, total_loss, final_state, train_step],
feed_dict={x: X, y: Y,
init_state: training_state})
training_loss += training_loss_
if step % 100 == 0 and step > 0:
if verbose:
print("Average loss at step", step, "for last 100 steps:", training_loss / 100)
training_losses.append(training_loss / 100)
training_loss = 0
return training_losses