Python高级:生成器(generator)

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

 

你可能感兴趣的:(C/C++,Java,Python)