Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all 9596 articles
Browse latest View live

深度有趣 | 12 一起来动动手

$
0
0

用TensorFlow实现一个手部实时检测器

和Inception-v3通过迁移学习实现定制的图片分类任务类似

在上节课内容的基础上,添加手部标注数据,并使用预训练好的模型完成迁移学习

数据

手部检测数据来自于

vision.soic.indiana.edu/projects/eg…

图片使用Google Class拍摄, egohands_data.zip 是一个压缩包,里面共有48个文件夹,分别对应48个不同场景(室内、室外、下棋等)中共计4800张标注图片,标注即全部的手部轮廓点


深度有趣 | 12 一起来动动手

不过我们不需要手动解压这个压缩包,而是使用代码去完成数据的解压和整理工作

egohands_dataset_clean.py 依次完成以下几项工作

如果当前目录下没有 egohands_data.zip 则下载,即调用 download_egohands_dataset() 否则解压 egohands_data.zip 并得到 egohands 文件夹,并对其中的图片数据执行 rename_files() rename_files() 会将所有的图片重命名,加上其父文件夹的名称,避免图片名重复,并调用 generate_csv_files() generate_csv_files() 读取每个场景下的图片,调用 get_bbox_visualize() ,根据标注文件 polygons.mat 绘制手部轮廓和Anchor Box并显示,同时将图片标注转换并存储为csv文件,全部处理完后,再调用 split_data_test_eval_train() split_data_test_eval_train() 完成训练集和测试集的分割,在 images 文件夹中新建 train 和 test 两个文件夹,分别存放对应的图片和csv标注 完成以上工作后,便可以手动删除一开始解压得到的 egohands 文件夹

也就是从 egohands_data.zip 得到 images 文件夹,在我的笔记本上共花费6分钟左右

接下来调用 generate_tfrecord.py ,将训练集和测试集整理成TFRecord文件

由于这里只需要检测手部,因此物体类别只有一种即 hand ,如果需要定制其他物体检测任务,修改以下代码即可

def class_text_to_int(row_label): if row_label == 'hand': return 1 else: None 复制代码

运行以下两条命令,生成训练集和测试集对应的TFRecord文件

python generate_tfrecord.py --csv_input=images/train/train_labels.csv --output_path=retrain/train.record 复制代码 python generate_tfrecord.py --csv_input=images/test/test_labels.csv --output_path=retrain/test.record 复制代码 模型

依旧是上节课使用的 ssd_mobilenet_v1_coco ,但这里只需要检测手部,所以需要根据定制的标注数据进行迁移学习

retrain 文件夹中内容如下

train.record 和 test.record :定制物体检测任务的标注数据 ssd_mobilenet_v1_coco_11_06_2017 :预训练好的 ssd_mobilenet_v1_coco 模型 ssd_mobilenet_v1_coco.config :使用迁移学习训练模型的配置文件 hand_label_map.pbtxt :指定检测类别的名称和编号映射 retrain.py :迁移学习的训练代码 object_detection :一些辅助文件

配置文件 ssd_mobilenet_v1_coco.config 的模版在这里

github.com/tensorflow/…

按需修改配置文件,主要是包括 PATH_TO_BE_CONFIGURED 的配置项

num_classes :物体类别的数量,这里为1 fine_tune_checkpoint :预训练好的模型checkpoint文件 train_input_reader :指定训练数据 input_path 和映射文件路径 label_map_path eval_input_reader :指定测试数据 input_path 和映射文件路径 label_map_path

映射文件 hand_label_map.pbtxt 内容如下,只有一个类别

item { id: 1 name: 'hand' } 复制代码

使用以下命令开始模型的迁移训练, train_dir 为模型输出路径, pipeline_config_path 为配置项路径

python retrain.py --logtostderr --train_dir=output/ --pipeline_config_path=ssd_mobilenet_v1_coco.config 复制代码

模型迁移训练完毕后,在 output 文件夹中即可看到生成的 .data 、 .index 、 .meta 等模型文件

使用TensorBoard查看模型训练过程,模型总损失如下

tensorboard --logdir='output' 复制代码
深度有趣 | 12 一起来动动手

最后,再使用 export_inference_graph.py 将模型打包成 .pb 文件

--pipeline_config_path :配置文件路径 --trained_checkpoint_prefix :模型checkpoint路径 --output_directory : .pb 文件输出路径 python export_inference_graph.py --input_type image_tensor --pipeline_config_path retrain/ssd_mobilenet_v1_coco.config --trained_checkpoint_prefix retrain/output/model.ckpt-153192 --output_directory hand_detection_inference_graph 复制代码

运行后会生成文件夹 hand_detection_inference_graph ,里面可以找到一个 frozen_inference_graph.pb 文件

应用

现在便可以使用训练好的手部检测模型,实现一个手部实时检测器

主要改动以下三行代码即可

PATH_TO_CKPT = 'hand_detection_inference_graph/frozen_inference_graph.pb' PATH_TO_LABELS = 'retrain/hand_label_map.pbtxt' NUM_CLASSES = 1 复制代码

完整代码如下

# -*- coding: utf-8 -*- import numpy as np import tensorflow as tf from utils import label_map_util from utils import visualization_utils as vis_util import cv2 cap = cv2.VideoCapture(0) PATH_TO_CKPT = 'hand_detection_inference_graph/frozen_inference_graph.pb' PATH_TO_LABELS = 'retrain/hand_label_map.pbtxt' NUM_CLASSES = 1 detection_graph = tf.Graph() with detection_graph.as_default(): od_graph_def = tf.GraphDef() with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid: od_graph_def.ParseFromString(fid.read()) tf.import_graph_def(od_graph_def, name='') label_map = label_map_util.load_labelmap(PATH_TO_LABELS) categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True) category_index = label_map_util.create_category_index(categories) with detection_graph.as_default(): with tf.Session(graph=detection_graph) as sess: image_tensor = detection_graph.get_tensor_by_name('image_tensor:0') detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0') detection_scores = detection_graph.get_tensor_by_name('detection_scores:0') detection_classes = detection_graph.get_tensor_by_name('detection_classes:0') num_detections = detection_graph.get_tensor_by_name('num_detections:0') while True: ret, image_np = cap.read() image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB) image_np_expanded = np.expand_dims(image_np, axis=0) (boxes, scores, classes, num) = sess.run( [detection_boxes, detection_scores, detection_classes, num_detections], feed_dict={image_tensor: image_np_expanded}) vis_util.visualize_boxes_and_labels_on_image_array(image_np, np.squeeze(boxes), np.squeeze(classes).astype(np.int32), np.squeeze(scores), category_index, use_normalized_coordinates=True, line_thickness=8) cv2.imshow('hand detection', cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)) if cv2.waitKey(25) & 0xFF == ord('q'): cap.release() cv2.destroyAllwindows() break 复制代码

运行代码后,即可看到摄像头中手部检测的结果


深度有趣 | 12 一起来动动手
定制检测任务

如果希望定制自己的检测任务,准备一些图片,然后手动标注,有个几百条就差不多了

使用 labelImg 进行图片标注,安装方法请参考以下链接

github.com/tzutalin/la…

进入 labelImg 文件夹,使用以下命令,两个参数分别表示图片目录和分类文件路径

python labelImg.py ../imgs/ ../predefined_classes.txt 复制代码

标注界面如下图所示,按 w 开始矩形的绘制,按 Ctrl+S 保存标注至 xml 文件夹


深度有趣 | 12 一起来动动手

之后运行 xml_to_csv.py 即可将 .xml 文件转为 .csv 文件

总之,为了准备TFRecord数据,按照以下步骤操作

新建 train 和 test 文件夹并分配图片 分别对训练集和测试集图片手工标注 将训练集和测试集对应的多个 .xml 转为一个 .csv 根据原始图片和 .csv 生成对应的TFRecord

The best way to implement an agent in Django applications

$
0
0

What I'm needing is something running in background, independently of web requests, continuously checking a specific table and performing some calculations. The term "agent" seems to fit better for this task, but I'm not sure if it's correct.

Any thoughts or ideas on how to implement this? The first thing that occurred me was a script being called by cron but I'm also not sure...

Thanks.

The most popular way these days is to use some sort of message queue, with a helper library like Celery. The way this works is that you will have a number of daemons running, listening on a message queue. In your application, you will add messages to the queue which will be picked up by the workers. This is pretty complex, but it's pretty fast.

However, it feels to me like completely overkill for your application. The cron way of doing things is much simpler and less fragile. It also makes for easier debugging and testing of your code.

As a heretic side note, if your use case really is "check a specific table and perform some calculations that go into some other table", you could use a database trigger to do the work.

深度有趣 | 16 令人拍案叫绝的WGAN

$
0
0

在DCGAN的基础上,介绍WGAN的原理和实现,并在LFW和CelebA两个数据集上进一步实践

问题

GAN一直面临以下问题和挑战

训练困难,需要精心设计模型结构,并小心协调G和D的训练程度 G和D的损失函数无法指示训练过程,缺乏一个有意义的指标和生成图片的质量相关联 模式崩坏(mode collapse),生成的图片虽然看起来像是真的,但是缺乏多样性 原理

相对于传统的GAN,WGAN只做了以下三点简单的改动

sigmoid_cross_entropy_with_logits

G的损失函数原本为

其导致的结果是,如果D训练得太好,G将学习不到有效的梯度

但是,如果D训练得不够好,G也学习不到有效的梯度

就像警察如果太厉害,便直接把小偷干掉了;但警察如果不厉害,就无法迫使小偷变得更厉害

因此以上损失函数导致GAN训练特别不稳定,需要小心协调G和D的训练程度

GAN的作者提出了G损失函数的另一个版本,即所谓的 -logD trick

G需要最小化以上损失函数,等价于最小化以下损失函数

其中前者为KL散度(Kullback Leibler Divergence)

后者为JS散度(Jensen-Shannon Divergence)

两者都可以用于衡量两个分布之间的距离,越小说明两个分布越相似

因此以上损失函数,一方面要减小KL散度,另一方面却要增大JS散度,一边拉近一边推远,从而导致训练不稳定

除此之外,KL散度的不对称性,导致对以下两种情况的不同惩罚

G生成了不真实的图片,即缺乏准确性,惩罚较高 G生成了和真实图片类似的图片,即缺乏多样性,惩罚较低

从而导致,G倾向于生成一些有把握但相似的图片,而不敢轻易地尝试去生成没把握的新图片,即所谓的mode collapse问题

WGAN所做的三点改动,解决了GAN训练困难和不稳定、mode collapse等问题,而且G的损失函数越小,对应生成的图片质量就越高

WGAN训练过程如下,gradient penalty使得D满足1-Lipschitz连续条件,详细原理和细节可以阅读相关论文进一步了解


深度有趣 | 16 令人拍案叫绝的WGAN

论文中部分实验结果如下,WGAN虽然需要更长的训练时间,但收敛更加稳定


深度有趣 | 16 令人拍案叫绝的WGAN

更重要的是,WGAN提供了一种更稳定的GAN框架。DCGAN中的G去掉Batch Normalization就会崩掉,但WGAN则没有这种限制

如果用Deep Convolutional结构实现WGAN,那么其结果和DCGAN差不多。但是在WGAN的框架下,可以用更深更复杂的网络实现G和D,例如ResNet( arxiv.org/abs/1512.03… ),从而达到更好的生成效果

数据

还是之前使用过的两个人脸数据集

LFW: vis-www.cs.umass.edu/lfw/ ,Labeled Faces in the Wild,包括1680人共计超过1.3W张图片 CelebA: mmlab.ie.cuhk.edu.hk/projects/Ce… ,CelebFaces Attributes Dataset,包括10177人共计超过20W张图片,并且每张图片还包括人脸的5个关键点位置和40个属性的01标注,例如是否有眼镜、帽子、胡子等 实现

加载库

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import os import matplotlib.pyplot as plt %matplotlib inline from imageio import imread, imsave, mimsave import cv2 import glob from tqdm import tqdm 复制代码

选择数据集

dataset = 'lfw_new_imgs' # LFW # dataset = 'celeba' # CelebA images = glob.glob(os.path.join(dataset, '*.*')) print(len(images)) 复制代码

定义一些常量、网络输入、辅助函数

batch_size = 100 z_dim = 100 WIDTH = 64 HEIGHT = 64 LAMBDA = 10 DIS_ITERS = 3 # 5 OUTPUT_DIR = 'samples_' + dataset if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) X = tf.placeholder(dtype=tf.float32, shape=[batch_size, HEIGHT, WIDTH, 3], name='X') noise = tf.placeholder(dtype=tf.float32, shape=[batch_size, z_dim], name='noise') is_training = tf.placeholder(dtype=tf.bool, name='is_training') def lrelu(x, leak=0.2): return tf.maximum(x, leak * x) 复制代码

判别器部分,注意需要去掉Batch Normalization,否则会导致batch之间的相关性,从而影响gradient penalty的计算

def discriminator(image, reuse=None, is_training=is_training): momentum = 0.9 with tf.variable_scope('discriminator', reuse=reuse): h0 = lrelu(tf.layers.conv2d(image, kernel_size=5, filters=64, strides=2, padding='same')) h1 = lrelu(tf.layers.conv2d(h0, kernel_size=5, filters=128, strides=2, padding='same')) h2 = lrelu(tf.layers.conv2d(h1, kernel_size=5, filters=256, strides=2, padding='same')) h3 = lrelu(tf.layers.conv2d(h2, kernel_size=5, filters=512, strides=2, padding='same')) h4 = tf.contrib.layers.flatten(h3) h4 = tf.layers.dense(h4, units=1) return h4 复制代码

生成器部分

def generator(z, is_training=is_training): momentum = 0.9 with tf.variable_scope('generator', reuse=None): d = 4 h0 = tf.layers.dense(z, units=d * d * 512) h0 = tf.reshape(h0, shape=[-1, d, d, 512]) h0 = tf.nn.relu(tf.contrib.layers.batch_norm(h0, is_training=is_training, decay=momentum)) h1 = tf.layers.conv2d_transpose(h0, kernel_size=5, filters=256, strides=2, padding='same') h1 = tf.nn.relu(tf.contrib.layers.batch_norm(h1, is_training=is_training, decay=momentum)) h2 = tf.layers.conv2d_transpose(h1, kernel_size=5, filters=128, strides=2, padding='same') h2 = tf.nn.relu(tf.contrib.layers.batch_norm(h2, is_training=is_training, decay=momentum)) h3 = tf.layers.conv2d_transpose(h2, kernel_size=5, filters=64, strides=2, padding='same') h3 = tf.nn.relu(tf.contrib.layers.batch_norm(h3, is_training=is_training, decay=momentum)) h4 = tf.layers.conv2d_transpose(h3, kernel_size=5, filters=3, strides=2, padding='same', activation=tf.nn.tanh, name='g') return h4 复制代码

损失函数

g = generator(noise) d_real = discriminator(X) d_fake = discriminator(g, reuse=True) loss_d_real = -tf.reduce_mean(d_real) loss_d_fake = tf.reduce_mean(d_fake) loss_g = -tf.reduce_mean(d_fake) loss_d = loss_d_real + loss_d_fake alpha = tf.random_uniform(shape=[batch_size, 1, 1, 1], minval=0., maxval=1.) interpolates = alpha * X + (1 - alpha) * g grad = tf.gradients(discriminator(interpolates, reuse=True), [interpolates])[0] slop = tf.sqrt(tf.reduce_sum(tf.square(grad), axis=[1])) gp = tf.reduce_mean((slop - 1.) ** 2) loss_d += LAMBDA * gp vars_g = [var for var in tf.trainable_variables() if var.name.startswith('generator')] vars_d = [var for var in tf.trainable_variables() if var.name.startswith('discriminator')] 复制代码

优化函数

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): optimizer_d = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_d, var_list=vars_d) optimizer_g = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_g, var_list=vars_g) 复制代码

读取图片的函数

def read_image(path, height, width): image = imread(path) h = image.shape[0] w = image.shape[1] if h > w: image = image[h // 2 - w // 2: h // 2 + w // 2, :, :] else: image = image[:, w // 2 - h // 2: w // 2 + h // 2, :] image = cv2.resize(image, (width, height)) return image / 255. 复制代码

合成图片的函数

def montage(images): if isinstance(images, list): images = np.array(images) img_h = images.shape[1] img_w = images.shape[2] n_plots = int(np.ceil(np.sqrt(images.shape[0]))) if len(images.shape) == 4 and images.shape[3] == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 3)) * 0.5 elif len(images.shape) == 4 and images.shape[3] == 1: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 1)) * 0.5 elif len(images.shape) == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1)) * 0.5 else: raise ValueError('Could not parse image shape of {}'.format(images.shape)) for i in range(n_plots): for j in range(n_plots): this_filter = i * n_plots + j if this_filter < images.shape[0]: this_img = images[this_filter] m[1 + i + i * img_h:1 + i + (i + 1) * img_h, 1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img return m 复制代码

随机产生批数据的函数

def get_random_batch(nums): img_index = np.arange(len(images)) np.random.shuffle(img_index) img_index = img_index[:nums] batch = np.array([read_image(images[i], HEIGHT, WIDTH) for i in img_index]) batch = (batch - 0.5) * 2 return batch 复制代码

模型的训练

sess = tf.Session() sess.run(tf.global_variables_initializer()) z_samples = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) samples = [] loss = {'d': [], 'g': []} for i in tqdm(range(60000)): for j in range(DIS_ITERS): n = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) batch = get_random_batch(batch_size) _, d_ls = sess.run([optimizer_d, loss_d], feed_dict={X: batch, noise: n, is_training: True}) _, g_ls = sess.run([optimizer_g, loss_g], feed_dict={X: batch, noise: n, is_training: True}) loss['d'].append(d_ls) loss['g'].append(g_ls) if i % 500 == 0: print(i, d_ls, g_ls) gen_imgs = sess.run(g, feed_dict={noise: z_samples, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, :] for img in gen_imgs] gen_imgs = montage(imgs) plt.axis('off') plt.imshow(gen_imgs) imsave(os.path.join(OUTPUT_DIR, 'sample_%d.jpg' % i), gen_imgs) plt.show() samples.append(gen_imgs) plt.plot(loss['d'], label='Discriminator') plt.plot(loss['g'], label='Generator') plt.legend(loc='upper right') plt.savefig(os.path.join(OUTPUT_DIR, 'Loss.png')) plt.show() mimsave(os.path.join(OUTPUT_DIR, 'samples.gif'), samples, fps=10) 复制代码

LFW人脸生成结果如下,和DCGAN相比更加稳定


深度有趣 | 16 令人拍案叫绝的WGAN

CelebA人脸生成结果如下


深度有趣 | 16 令人拍案叫绝的WGAN

保存模型,便于后续使用

saver = tf.train.Saver() saver.save(sess, os.path.join(OUTPUT_DIR, 'wgan_' + dataset), global_step=60000) 复制代码

在单机上使用模型生成人脸图片

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import matplotlib.pyplot as plt import os batch_size = 100 z_dim = 100 # dataset = 'lfw_new_imgs' dataset = 'celeba' def montage(images): if isinstance(images, list): images = np.array(images) img_h = images.shape[1] img_w = images.shape[2] n_plots = int(np.ceil(np.sqrt(images.shape[0]))) if len(images.shape) == 4 and images.shape[3] == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 3)) * 0.5 elif len(images.shape) == 4 and images.shape[3] == 1: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 1)) * 0.5 elif len(images.shape) == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1)) * 0.5 else: raise ValueError('Could not parse image shape of {}'.format(images.shape)) for i in range(n_plots): for j in range(n_plots): this_filter = i * n_plots + j if this_filter < images.shape[0]: this_img = images[this_filter] m[1 + i + i * img_h:1 + i + (i + 1) * img_h, 1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img return m sess = tf.Session() sess.run(tf.global_variables_initializer()) saver = tf.train.import_meta_graph(os.path.join('samples_' + dataset, 'wgan_' + dataset + '-60000.meta')) saver.restore(sess, tf.train.latest_checkpoint('samples_' + dataset)) graph = tf.get_default_graph() g = graph.get_tensor_by_name('generator/g/Tanh:0') noise = graph.get_tensor_by_name('noise:0') is_training = graph.get_tensor_by_name('is_training:0') n = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) gen_imgs = sess.run(g, feed_dict={noise: n, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, :] for img in gen_imgs] gen_imgs = montage(imgs) gen_imgs = np.clip(gen_imgs, 0, 1) plt.figure(figsize=(8, 8)) plt.axis('off') plt.imshow(gen_imgs) plt.show() 复制代码

深度有趣 | 17 CGAN和ACGAN

$
0
0

介绍CGAN和ACGAN的原理,通过引入额外的Condition来控制生成的图片,并在DCGAN和WGAN的基础上进行实现

CGAN原理

样本x可以包含一些属性,或者说条件,记作y

例如MNIST中每张图片对应的数字可以是0至9

从一张图来了解CGAN(Conditional GAN)的思想


深度有趣 | 17 CGAN和ACGAN

生成器G从随机噪音z和条件y生成假样本,判别器D接受真假样本和条件y,判断样本是否为满足条件y的真实样本

总的目标函数如下

实现

先用MNIST,在DCGAN的基础上稍作改动以实现CGAN

加载库

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import matplotlib.pyplot as plt %matplotlib inline import os, imageio from tqdm import tqdm 复制代码

加载数据,指定 one_hot=True

from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True) 复制代码

定义一些常量、网络输入、辅助函数,这里加上了 y_label 和 y_noise

batch_size = 100 z_dim = 100 WIDTH = 28 HEIGHT = 28 LABEL = 10 OUTPUT_DIR = 'samples' if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) X = tf.placeholder(dtype=tf.float32, shape=[None, HEIGHT, WIDTH, 1], name='X') y_label = tf.placeholder(dtype=tf.float32, shape=[None, HEIGHT, WIDTH, LABEL], name='y_label') noise = tf.placeholder(dtype=tf.float32, shape=[None, z_dim], name='noise') y_noise = tf.placeholder(dtype=tf.float32, shape=[None, LABEL], name='y_noise') is_training = tf.placeholder(dtype=tf.bool, name='is_training') def lrelu(x, leak=0.2): return tf.maximum(x, leak * x) def sigmoid_cross_entropy_with_logits(x, y): return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, labels=y) 复制代码

判别器部分

def discriminator(image, label, reuse=None, is_training=is_training): momentum = 0.9 with tf.variable_scope('discriminator', reuse=reuse): h0 = tf.concat([image, label], axis=3) h0 = lrelu(tf.layers.conv2d(h0, kernel_size=5, filters=64, strides=2, padding='same')) h1 = tf.layers.conv2d(h0, kernel_size=5, filters=128, strides=2, padding='same') h1 = lrelu(tf.contrib.layers.batch_norm(h1, is_training=is_training, decay=momentum)) h2 = tf.layers.conv2d(h1, kernel_size=5, filters=256, strides=2, padding='same') h2 = lrelu(tf.contrib.layers.batch_norm(h2, is_training=is_training, decay=momentum)) h3 = tf.layers.conv2d(h2, kernel_size=5, filters=512, strides=2, padding='same') h3 = lrelu(tf.contrib.layers.batch_norm(h3, is_training=is_training, decay=momentum)) h4 = tf.contrib.layers.flatten(h3) h4 = tf.layers.dense(h4, units=1) return tf.nn.sigmoid(h4), h4 复制代码

生成器部分

def generator(z, label, is_training=is_training): momentum = 0.9 with tf.variable_scope('generator', reuse=None): d = 3 z = tf.concat([z, label], axis=1) h0 = tf.layers.dense(z, units=d * d * 512) h0 = tf.reshape(h0, shape=[-1, d, d, 512]) h0 = tf.nn.relu(tf.contrib.layers.batch_norm(h0, is_training=is_training, decay=momentum)) h1 = tf.layers.conv2d_transpose(h0, kernel_size=5, filters=256, strides=2, padding='same') h1 = tf.nn.relu(tf.contrib.layers.batch_norm(h1, is_training=is_training, decay=momentum)) h2 = tf.layers.conv2d_transpose(h1, kernel_size=5, filters=128, strides=2, padding='same') h2 = tf.nn.relu(tf.contrib.layers.batch_norm(h2, is_training=is_training, decay=momentum)) h3 = tf.layers.conv2d_transpose(h2, kernel_size=5, filters=64, strides=2, padding='same') h3 = tf.nn.relu(tf.contrib.layers.batch_norm(h3, is_training=is_training, decay=momentum)) h4 = tf.layers.conv2d_transpose(h3, kernel_size=5, filters=1, strides=1, padding='valid', activation=tf.nn.tanh, name='g') return h4 复制代码

损失函数

g = generator(noise, y_noise) d_real, d_real_logits = discriminator(X, y_label) d_fake, d_fake_logits = discriminator(g, y_label, reuse=True) vars_g = [var for var in tf.trainable_variables() if var.name.startswith('generator')] vars_d = [var for var in tf.trainable_variables() if var.name.startswith('discriminator')] loss_d_real = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_real_logits, tf.ones_like(d_real))) loss_d_fake = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_fake_logits, tf.zeros_like(d_fake))) loss_g = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_fake_logits, tf.ones_like(d_fake))) loss_d = loss_d_real + loss_d_fake 复制代码

优化函数

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): optimizer_d = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_d, var_list=vars_d) optimizer_g = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_g, var_list=vars_g) 复制代码

拼接图片的函数

def montage(images): if isinstance(images, list): images = np.array(images) img_h = images.shape[1] img_w = images.shape[2] n_plots = int(np.ceil(np.sqrt(images.shape[0]))) m = np.ones((images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1)) * 0.5 for i in range(n_plots): for j in range(n_plots): this_filter = i * n_plots + j if this_filter < images.shape[0]: this_img = images[this_filter] m[1 + i + i * img_h:1 + i + (i + 1) * img_h, 1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img return m 复制代码

训练模型,加入条件信息

sess = tf.Session() sess.run(tf.global_variables_initializer()) z_samples = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) y_samples = np.zeros([batch_size, LABEL]) for i in range(LABEL): for j in range(LABEL): y_samples[i * LABEL + j, i] = 1 samples = [] loss = {'d': [], 'g': []} for i in tqdm(range(60000)): n = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) batch, label = mnist.train.next_batch(batch_size=batch_size) batch = np.reshape(batch, [batch_size, HEIGHT, WIDTH, 1]) batch = (batch - 0.5) * 2 yn = np.copy(label) yl = np.reshape(label, [batch_size, 1, 1, LABEL]) yl = yl * np.ones([batch_size, HEIGHT, WIDTH, LABEL]) d_ls, g_ls = sess.run([loss_d, loss_g], feed_dict={X: batch, noise: n, y_label: yl, y_noise: yn, is_training: True}) loss['d'].append(d_ls) loss['g'].append(g_ls) sess.run(optimizer_d, feed_dict={X: batch, noise: n, y_label: yl, y_noise: yn, is_training: True}) sess.run(optimizer_g, feed_dict={X: batch, noise: n, y_label: yl, y_noise: yn, is_training: True}) sess.run(optimizer_g, feed_dict={X: batch, noise: n, y_label: yl, y_noise: yn, is_training: True}) if i % 1000 == 0: print(i, d_ls, g_ls) gen_imgs = sess.run(g, feed_dict={noise: z_samples, y_noise: y_samples, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, 0] for img in gen_imgs] gen_imgs = montage(imgs) plt.axis('off') plt.imshow(gen_imgs, cmap='gray') imageio.imsave(os.path.join(OUTPUT_DIR, 'sample_%d.jpg' % i), gen_imgs) plt.show() samples.append(gen_imgs) plt.plot(loss['d'], label='Discriminator') plt.plot(loss['g'], label='Generator') plt.legend(loc='upper right') plt.savefig('Loss.png') plt.show() imageio.mimsave(os.path.join(OUTPUT_DIR, 'samples.gif'), samples, fps=5) 复制代码

生成的手写数字图片如下,每一行对应的数字相同


深度有趣 | 17 CGAN和ACGAN

保存模型,便于后续使用

saver = tf.train.Saver() saver.save(sess, './mnist_cgan', global_step=60000) 复制代码

在单机上使用模型生成手写数字图片

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import matplotlib.pyplot as plt batch_size = 100 z_dim = 100 LABEL = 10 def montage(images): if isinstance(images, list): images = np.array(images) img_h = images.shape[1] img_w = images.shape[2] n_plots = int(np.ceil(np.sqrt(images.shape[0]))) m = np.ones((images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1)) * 0.5 for i in range(n_plots): for j in range(n_plots): this_filter = i * n_plots + j if this_filter < images.shape[0]: this_img = images[this_filter] m[1 + i + i * img_h:1 + i + (i + 1) * img_h, 1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img return m sess = tf.Session() sess.run(tf.global_variables_initializer()) saver = tf.train.import_meta_graph('./mnist_cgan-60000.meta') saver.restore(sess, tf.train.latest_checkpoint('./')) graph = tf.get_default_graph() g = graph.get_tensor_by_name('generator/g/Tanh:0') noise = graph.get_tensor_by_name('noise:0') y_noise = graph.get_tensor_by_name('y_noise:0') is_training = graph.get_tensor_by_name('is_training:0') n = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) y_samples = np.zeros([batch_size, LABEL]) for i in range(LABEL): for j in range(LABEL): y_samples[i * LABEL + j, i] = 1 gen_imgs = sess.run(g, feed_dict={noise: n, y_noise: y_samples, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, 0] for img in gen_imgs] gen_imgs = montage(imgs) plt.axis('off') plt.imshow(gen_imgs, cmap='gray') plt.show() 复制代码 讲条件的CelebA

了解CGAN的原理和实现之后,再尝试下别的数据集,比如之前用过的CelebA

CelebA提供了每张图片40个属性的01标注,这里将Male(是否为男性)作为条件,在WGAN的基础上实现CGAN

加载库

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import os import matplotlib.pyplot as plt %matplotlib inline from imageio import imread, imsave, mimsave import cv2 import glob from tqdm import tqdm 复制代码

加载图片

images = glob.glob('celeba/*.jpg') print(len(images)) 复制代码

读取图片的Male标签

tags = {} target = 'Male' with open('list_attr_celeba.txt', 'r') as fr: lines = fr.readlines() all_tags = lines[0].strip('\n').split() for i in range(1, len(lines)): line = lines[i].strip('\n').split() if int(line[all_tags.index(target) + 1]) == 1: tags[line[0]] = [1, 0] # 男 else: tags[line[0]] = [0, 1] # 女 print(len(tags)) print(all_tags) 复制代码

定义一些常量、网络输入、辅助函数

batch_size = 100 z_dim = 100 WIDTH = 64 HEIGHT = 64 LABEL = 2 LAMBDA = 10 DIS_ITERS = 3 # 5 OUTPUT_DIR = 'samples' if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) X = tf.placeholder(dtype=tf.float32, shape=[batch_size, HEIGHT, WIDTH, 3], name='X') y_label = tf.placeholder(dtype=tf.float32, shape=[batch_size, HEIGHT, WIDTH, LABEL], name='y_label') noise = tf.placeholder(dtype=tf.float32, shape=[batch_size, z_dim], name='noise') y_noise = tf.placeholder(dtype=tf.float32, shape=[batch_size, LABEL], name='y_noise') is_training = tf.placeholder(dtype=tf.bool, name='is_training') def lrelu(x, leak=0.2): return tf.maximum(x, leak * x) 复制代码

判别器部分

def discriminator(image, label, reuse=None, is_training=is_training): momentum = 0.9 with tf.variable_scope('discriminator', reuse=reuse): h0 = tf.concat([image, label], axis=3) h0 = lrelu(tf.layers.conv2d(h0, kernel_size=5, filters=64, strides=2, padding='same')) h1 = lrelu(tf.layers.conv2d(h0, kernel_size=5, filters=128, strides=2, padding='same')) h2 = lrelu(tf.layers.conv2d(h1, kernel_size=5, filters=256, strides=2, padding='same')) h3 = lrelu(tf.layers.conv2d(h2, kernel_size=5, filters=512, strides=2, padding='same')) h4 = tf.contrib.layers.flatten(h3) h4 = tf.layers.dense(h4, units=1) return h4 复制代码

生成器部分

def generator(z, label, is_training=is_training): momentum = 0.9 with tf.variable_scope('generator', reuse=None): d = 4 z = tf.concat([z, label], axis=1) h0 = tf.layers.dense(z, units=d * d * 512) h0 = tf.reshape(h0, shape=[-1, d, d, 512]) h0 = tf.nn.relu(tf.contrib.layers.batch_norm(h0, is_training=is_training, decay=momentum)) h1 = tf.layers.conv2d_transpose(h0, kernel_size=5, filters=256, strides=2, padding='same') h1 = tf.nn.relu(tf.contrib.layers.batch_norm(h1, is_training=is_training, decay=momentum)) h2 = tf.layers.conv2d_transpose(h1, kernel_size=5, filters=128, strides=2, padding='same') h2 = tf.nn.relu(tf.contrib.layers.batch_norm(h2, is_training=is_training, decay=momentum)) h3 = tf.layers.conv2d_transpose(h2, kernel_size=5, filters=64, strides=2, padding='same') h3 = tf.nn.relu(tf.contrib.layers.batch_norm(h3, is_training=is_training, decay=momentum)) h4 = tf.layers.conv2d_transpose(h3, kernel_size=5, filters=3, strides=2, padding='same', activation=tf.nn.tanh, name='g') return h4 复制代码

定义损失函数

g = generator(noise, y_noise) d_real = discriminator(X, y_label) d_fake = discriminator(g, y_label, reuse=True) loss_d_real = -tf.reduce_mean(d_real) loss_d_fake = tf.reduce_mean(d_fake) loss_g = -tf.reduce_mean(d_fake) loss_d = loss_d_real + loss_d_fake alpha = tf.random_uniform(shape=[batch_size, 1, 1, 1], minval=0., maxval=1.) interpolates = alpha * X + (1 - alpha) * g grad = tf.gradients(discriminator(interpolates, y_label, reuse=True), [interpolates])[0] slop = tf.sqrt(tf.reduce_sum(tf.square(grad), axis=[1])) gp = tf.reduce_mean((slop - 1.) ** 2) loss_d += LAMBDA * gp vars_g = [var for var in tf.trainable_variables() if var.name.startswith('generator')] vars_d = [var for var in tf.trainable_variables() if var.name.startswith('discriminator')] 复制代码

定义优化器

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): optimizer_d = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_d, var_list=vars_d) optimizer_g = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_g, var_list=vars_g) 复制代码

拼接图片的函数

def montage(images): if isinstance(images, list): images = np.array(images) img_h = images.shape[1] img_w = images.shape[2] n_plots = int(np.ceil(np.sqrt(images.shape[0]))) if len(images.shape) == 4 and images.shape[3] == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 3)) * 0.5 elif len(images.shape) == 4 and images.shape[3] == 1: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 1)) * 0.5 elif len(images.shape) == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1)) * 0.5 else: raise ValueError('Could not parse image shape of {}'.format(images.shape)) for i in range(n_plots): for j in range(n_plots): this_filter = i * n_plots + j if this_filter < images.shape[0]: this_img = images[this_filter] m[1 + i + i * img_h:1 + i + (i + 1) * img_h, 1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img return m 复制代码

整理数据

X_all = [] Y_all = [] for i in tqdm(range(len(images))): image = imread(images[i]) h = image.shape[0] w = image.shape[1] if h > w: image = image[h // 2 - w // 2: h // 2 + w // 2, :, :] else: image = image[:, w // 2 - h // 2: w // 2 + h // 2, :] image = cv2.resize(image, (WIDTH, HEIGHT)) image = (image / 255. - 0.5) * 2 X_all.append(image) image_name = images[i][images[i].find('/') + 1:] Y_all.append(tags[image_name]) X_all = np.array(X_all) Y_all = np.array(Y_all) print(X_all.shape, Y_all.shape) 复制代码

查看数据样例

for i in range(10): plt.imshow((X_all[i, :, :, :] + 1) / 2) plt.show() print(Y_all[i, :]) 复制代码

定义随机产生批数据的函数

def get_random_batch(): data_index = np.arange(X_all.shape[0]) np.random.shuffle(data_index) data_index = data_index[:batch_size] X_batch = X_all[data_index, :, :, :] Y_batch = Y_all[data_index, :] yn = np.copy(Y_batch) yl = np.reshape(Y_batch, [batch_size, 1, 1, LABEL]) yl = yl * np.ones([batch_size, HEIGHT, WIDTH, LABEL]) return X_batch, yn, yl 复制代码

训练模型

sess = tf.Session() sess.run(tf.global_variables_initializer()) zs = np.random.uniform(-1.0, 1.0, [batch_size // 2, z_dim]).astype(np.float32) z_samples = [] y_samples = [] for i in range(batch_size // 2): z_samples.append(zs[i, :]) y_samples.append([1, 0]) z_samples.append(zs[i, :]) y_samples.append([0, 1]) samples = [] loss = {'d': [], 'g': []} for i in tqdm(range(60000)): for j in range(DIS_ITERS): n = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) X_batch, yn, yl = get_random_batch() _, d_ls = sess.run([optimizer_d, loss_d], feed_dict={X: X_batch, noise: n, y_label: yl, y_noise: yn, is_training: True}) _, g_ls = sess.run([optimizer_g, loss_g], feed_dict={X: X_batch, noise: n, y_label: yl, y_noise: yn, is_training: True}) loss['d'].append(d_ls) loss['g'].append(g_ls) if i % 500 == 0: print(i, d_ls, g_ls) gen_imgs = sess.run(g, feed_dict={noise: z_samples, y_noise: y_samples, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, :] for img in gen_imgs] gen_imgs = montage(imgs) plt.axis('off') plt.imshow(gen_imgs) imsave(os.path.join(OUTPUT_DIR, 'sample_%d.jpg' % i), gen_imgs) plt.show() samples.append(gen_imgs) plt.plot(loss['d'], label='Discriminator') plt.plot(loss['g'], label='Generator') plt.legend(loc='upper right') plt.savefig('Loss.png') plt.show() mimsave(os.path.join(OUTPUT_DIR, 'samples.gif'), samples, fps=10) 复制代码

结果如下,对于每一组图片,噪音部分相同但条件不同,男左女右


深度有趣 | 17 CGAN和ACGAN

保存模型

saver = tf.train.Saver() saver.save(sess, './celeba_cgan', global_step=60000) 复制代码 ACGAN

再通过一张图了解ACGAN(Auxiliary Classifier GAN)的原理


深度有趣 | 17 CGAN和ACGAN

和CGAN不同的是,C不直接输入D。D不仅需要判断每个样本的真假,还需要完成一个分类任务即预测C,通过增加一个辅助分类器实现

对D而言,损失函数如下

对G而言,损失函数如下

还是以CelebA的Male作为条件,在WGAN的基础上实现ACGAN

加载库

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import os import matplotlib.pyplot as plt %matplotlib inline from imageio import imread, imsave, mimsave import cv2 import glob from tqdm import tqdm 复制代码

加载图片

images = glob.glob('celeba/*.jpg') print(len(images)) 复制代码

读取图片的Male标签

tags = {} target = 'Male' with open('list_attr_celeba.txt', 'r') as fr: lines = fr.readlines() all_tags = lines[0].strip('\n').split() for i in range(1, len(lines)): line = lines[i].strip('\n').split() if int(line[all_tags.index(target) + 1]) == 1: tags[line[0]] = [1, 0] # 男 else: tags[line[0]] = [0, 1] # 女 print(len(tags)) print(all_tags) 复制代码

定义一些常量、网络输入、辅助函数

batch_size = 100 z_dim = 100 WIDTH = 64 HEIGHT = 64 LABEL = 2 LAMBDA = 10 DIS_ITERS = 3 # 5 OUTPUT_DIR = 'samples' if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) X = tf.placeholder(dtype=tf.float32, shape=[batch_size, HEIGHT, WIDTH, 3], name='X') Y = tf.placeholder(dtype=tf.float32, shape=[batch_size, LABEL], name='Y') noise = tf.placeholder(dtype=tf.float32, shape=[batch_size, z_dim], name='noise') is_training = tf.placeholder(dtype=tf.bool, name='is_training') def lrelu(x, leak=0.2): return tf.maximum(x, leak * x) 复制代码

判别器部分,去掉条件输入,增加分类输出

def discriminator(image, reuse=None, is_training=is_training): momentum = 0.9 with tf.variable_scope('discriminator', reuse=reuse): h0 = lrelu(tf.layers.conv2d(image, kernel_size=5, filters=64, strides=2, padding='same')) h1 = lrelu(tf.layers.conv2d(h0, kernel_size=5, filters=128, strides=2, padding='same')) h2 = lrelu(tf.layers.conv2d(h1, kernel_size=5, filters=256, strides=2, padding='same')) h3 = lrelu(tf.layers.conv2d(h2, kernel_size=5, filters=512, strides=2, padding='same')) h4 = tf.contrib.layers.flatten(h3) Y_ = tf.layers.dense(h4, units=LABEL) h4 = tf.layers.dense(h4, units=1) return h4, Y_ 复制代码

生成器部分,没有任何改动

def generator(z, label, is_training=is_training): momentum = 0.9 with tf.variable_scope('generator', reuse=None): d = 4 z = tf.concat([z, label], axis=1) h0 = tf.layers.dense(z, units=d * d * 512) h0 = tf.reshape(h0, shape=[-1, d, d, 512]) h0 = tf.nn.relu(tf.contrib.layers.batch_norm(h0, is_training=is_training, decay=momentum)) h1 = tf.layers.conv2d_transpose(h0, kernel_size=5, filters=256, strides=2, padding='same') h1 = tf.nn.relu(tf.contrib.layers.batch_norm(h1, is_training=is_training, decay=momentum)) h2 = tf.layers.conv2d_transpose(h1, kernel_size=5, filters=128, strides=2, padding='same') h2 = tf.nn.relu(tf.contrib.layers.batch_norm(h2, is_training=is_training, decay=momentum)) h3 = tf.layers.conv2d_transpose(h2, kernel_size=5, filters=64, strides=2, padding='same') h3 = tf.nn.relu(tf.contrib.layers.batch_norm(h3, is_training=is_training, decay=momentum)) h4 = tf.layers.conv2d_transpose(h3, kernel_size=5, filters=3, strides=2, padding='same', activation=tf.nn.tanh, name='g') return h4 复制代码

定义损失函数,加上分类部分对应的损失。理论上分类问题应该用交叉熵作为损失函数,这里使用MSE效果也不错

g = generator(noise, Y) d_real, y_real = discriminator(X) d_fake, y_fake = discriminator(g, reuse=True) loss_d_real = -tf.reduce_mean(d_real) loss_d_fake = tf.reduce_mean(d_fake) loss_cls_real = tf.losses.mean_squared_error(Y, y_real) loss_cls_fake = tf.losses.mean_squared_error(Y, y_fake) loss_d = loss_d_real + loss_d_fake + loss_cls_real loss_g = -tf.reduce_mean(d_fake) + loss_cls_fake alpha = tf.random_uniform(shape=[batch_size, 1, 1, 1], minval=0., maxval=1.) interpolates = alpha * X + (1 - alpha) * g grad = tf.gradients(discriminator(interpolates, reuse=True), [interpolates])[0] slop = tf.sqrt(tf.reduce_sum(tf.square(grad), axis=[1])) gp = tf.reduce_mean((slop - 1.) ** 2) loss_d += LAMBDA * gp vars_g = [var for var in tf.trainable_variables() if var.name.startswith('generator')] vars_d = [var for var in tf.trainable_variables() if var.name.startswith('discriminator')] 复制代码

定义优化器

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): optimizer_d = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_d, var_list=vars_d) optimizer_g = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_g, var_list=vars_g) 复制代码

拼接图片的函数

def montage(images): if isinstance(images, list): images = np.array(images) img_h = images.shape[1] img_w = images.shape[2] n_plots = int(np.ceil(np.sqrt(images.shape[0]))) if len(images.shape) == 4 and images.shape[3] == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 3)) * 0.5 elif len(images.shape) == 4 and images.shape[3] == 1: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 1)) * 0.5 elif len(images.shape) == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1)) * 0.5 else: raise ValueError('Could not parse image shape of {}'.format(images.shape)) for i in range(n_plots): for j in range(n_plots): this_filter = i * n_plots + j if this_filter < images.shape[0]: this_img = images[this_filter] m[1 + i + i * img_h:1 + i + (i + 1) * img_h, 1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img return m 复制代码

整理数据

X_all = [] Y_all = [] for i in tqdm(range(len(images))): image = imread(images[i]) h = image.shape[0] w = image.shape[1] if h > w: image = image[h // 2 - w // 2: h // 2 + w // 2, :, :] else: image = image[:, w // 2 - h // 2: w // 2 + h // 2, :] image = cv2.resize(image, (WIDTH, HEIGHT)) image = (image / 255. - 0.5) * 2 X_all.append(image) image_name = images[i][images[i].find('/') + 1:] Y_all.append(tags[image_name]) X_all = np.array(X_all) Y_all = np.array(Y_all) print(X_all.shape, Y_all.shape) 复制代码

查看数据样例

for i in range(10): plt.imshow((X_all[i, :, :, :] + 1) / 2) plt.show() print(Y_all[i, :]) 复制代码

定义随机产生批数据的函数

def get_random_batch(): data_index = np.arange(X_all.shape[0]) np.random.shuffle(data_index) data_index = data_index[:batch_size] X_batch = X_all[data_index, :, :, :] Y_batch = Y_all[data_index, :] return X_batch, Y_batch 复制代码

训练模型,根据ACGAN作相应调整

sess = tf.Session() sess.run(tf.global_variables_initializer()) zs = np.random.uniform(-1.0, 1.0, [batch_size // 2, z_dim]).astype(np.float32) z_samples = [] y_samples = [] for i in range(batch_size // 2): z_samples.append(zs[i, :]) y_samples.append([1, 0]) z_samples.append(zs[i, :]) y_samples.append([0, 1]) samples = [] loss = {'d': [], 'g': []} for i in tqdm(range(60000)): for j in range(DIS_ITERS): n = np.random.uniform(-1.0, 1.0, [batch_size, z_dim]).astype(np.float32) X_batch, Y_batch = get_random_batch() _, d_ls = sess.run([optimizer_d, loss_d], feed_dict={X: X_batch, Y: Y_batch, noise: n, is_training: True}) _, g_ls = sess.run([optimizer_g, loss_g], feed_dict={X: X_batch, Y: Y_batch, noise: n, is_training: True}) loss['d'].append(d_ls) loss['g'].append(g_ls) if i % 500 == 0: print(i, d_ls, g_ls) gen_imgs = sess.run(g, feed_dict={noise: z_samples, Y: y_samples, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, :] for img in gen_imgs] gen_imgs = montage(imgs) plt.axis('off') plt.imshow(gen_imgs) imsave(os.path.join(OUTPUT_DIR, 'sample_%d.jpg' % i), gen_imgs) plt.show() samples.append(gen_imgs) plt.plot(loss['d'], label='Discriminator') plt.plot(loss['g'], label='Generator') plt.legend(loc='upper right') plt.savefig('Loss.png') plt.show() mimsave(os.path.join(OUTPUT_DIR, 'samples.gif'), samples, fps=10) 复制代码

结果如下,比CGAN的结果好一些,崩掉的情况比较少,而且人脸更真实更清晰


深度有趣 | 17 CGAN和ACGAN

保存模型

saver = tf.train.Saver() saver.save(sess, './celeba_acgan', global_step=60000) 复制代码

在单机上加载模型,进行以下两个尝试:

固定噪音,渐变条件; 固定条件,渐变噪音。 # -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import matplotlib.pyplot as plt batch_size = 100 z_dim = 100 LABEL = 2 def montage(images): if isinstance(images, list): images = np.array(images) img_h = images.shape[1] img_w = images.shape[2] n_plots = int(np.ceil(np.sqrt(images.shape[0]))) if len(images.shape) == 4 and images.shape[3] == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 3)) * 0.5 elif len(images.shape) == 4 and images.shape[3] == 1: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1, 1)) * 0.5 elif len(images.shape) == 3: m = np.ones( (images.shape[1] * n_plots + n_plots + 1, images.shape[2] * n_plots + n_plots + 1)) * 0.5 else: raise ValueError('Could not parse image shape of {}'.format(images.shape)) for i in range(n_plots): for j in range(n_plots): this_filter = i * n_plots + j if this_filter < images.shape[0]: this_img = images[this_filter] m[1 + i + i * img_h:1 + i + (i + 1) * img_h, 1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img return m sess = tf.Session() sess.run(tf.global_variables_initializer()) saver = tf.train.import_meta_graph('./celeba_acgan-60000.meta') saver.restore(sess, tf.train.latest_checkpoint('./')) graph = tf.get_default_graph() g = graph.get_tensor_by_name('generator/g/Tanh:0') noise = graph.get_tensor_by_name('noise:0') Y = graph.get_tensor_by_name('Y:0') is_training = graph.get_tensor_by_name('is_training:0') # 固定噪音,渐变条件 n = np.random.uniform(-1.0, 1.0, [10, z_dim]).astype(np.float32) ns = [] y_samples = [] for i in range(100): ns.append(n[i // 10, :]) y_samples.append([i % 10 / 9, 1 - i % 10 / 9]) gen_imgs = sess.run(g, feed_dict={noise: ns, Y: y_samples, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, :] for img in gen_imgs] gen_imgs = montage(imgs) gen_imgs = np.clip(gen_imgs, 0, 1) plt.figure(figsize=(8, 8)) plt.axis('off') plt.imshow(gen_imgs) plt.show() # 固定条件,渐变噪音 n = np.random.uniform(-1.0, 1.0, [5, 2, z_dim]).astype(np.float32) ns = [] y_samples = [] for i in range(5): for k in range(2): for j in range(10): start = n[i, 0, :] end = n[i, 1, :] ns.append(start + j * (end - start) / 9) if k == 0: y_samples.append([0, 1]) else: y_samples.append([1, 0]) gen_imgs = sess.run(g, feed_dict={noise: ns, Y: y_samples, is_training: False}) gen_imgs = (gen_imgs + 1) / 2 imgs = [img[:, :, :] for img in gen_imgs] gen_imgs = montage(imgs) gen_imgs = np.clip(gen_imgs, 0, 1) plt.figure(figsize=(8, 8)) plt.axis('off') plt.imshow(gen_imgs) plt.show() 复制代码

由女变男的过程


深度有趣 | 17 CGAN和ACGAN

人脸两两之间的渐变


深度有趣 | 17 CGAN和ACGAN

深度有趣 | 19 pix2pix图像翻译

$
0
0

配对图像翻译包括很多应用场景,输入和输出都是图片且尺寸相同

街道标注,街道实景 楼房标注,楼房实景 黑白图片,上色图片 卫星地图,简易地图 白天,夜晚 边缘,实物
深度有趣 | 19 pix2pix图像翻译

pix2pix提供了一种通用的技术框架,用于完成各种配对图像翻译任务

作者还提供了一个在线Demo,包括曾经火爆一时的edge2cat, affinelayer.com/pixsrv/

pix2pix原理如下,典型的CGAN结构,但G只接受一个固定的输入X,可以理解为一个条件C,即不需要随机噪音,然后输出翻译后的版本Y

D接受一个X(CGAN中的C)和一个Y(真假样本),并判断X和Y是否为配对的翻译


深度有趣 | 19 pix2pix图像翻译

除了标准的GAN损失函数之外,pix2pix还考虑了生成样本和真实样本之间的L1距离作为损失

GAN损失负责捕捉图像高频特征,L1损失负责捕捉低频特征,使得生成结果既真实且清晰

生成器G使用Unet实现,主要用到Skip-Connection来学习配对图像之间的映射


深度有趣 | 19 pix2pix图像翻译

判别器D使用了PatchGAN的思想,之前是对整张图片给出一个分数,PatchGAN则是将一张图片分为很多块,对每一块都给出一个分数

实现

代码参考自以下项目, github.com/affinelayer… ,提供了很多方便好用的功能

多个预训练好的模型,可用于完成各种图像翻译任务 在自己的配对图像数据上训练图像翻译模型(两个文件夹,对应图片的名称和尺寸相同) 在自己的图像数据上训练上色模型(一个文件夹存放彩色图片即可,因为黑白图片可以从彩色图片中自动获取)

数据集下载链接, people.eecs.berkeley.edu/~tinghuiz/p… ,包括五个数据集:楼房、街景、地图、鞋子、包

以facades楼房数据为例,train、val、test分别包括400、100、106张图片,每张图片包括两部分,对应翻译前后的两个版本


深度有趣 | 19 pix2pix图像翻译

加载库

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np import matplotlib.pyplot as plt %matplotlib inline from imageio import imread, imsave, mimsave import glob import os from tqdm import tqdm 复制代码

加载图片,使用train和val,共500张图片

images = glob.glob('data/train/*.jpg') + glob.glob('data/val/*.jpg') print(len(images)) 复制代码

整理数据,从每张图片中分离出X和Y,B2A表示从右往左

X_all = [] Y_all = [] WIDTH = 256 HEIGHT = 256 for image in images: img = imread(image) img = (img / 255. - 0.5) * 2 # B2A X_all.append(img[:, WIDTH:, :]) Y_all.append(img[:, :WIDTH, :]) X_all = np.array(X_all) Y_all = np.array(Y_all) print(X_all.shape, Y_all.shape) 复制代码

定义一些常量、网络tensor、辅助函数,这里的 batch_size 设为1,因此每次训练都是一对一的图像翻译

batch_size = 1 LAMBDA = 100 OUTPUT_DIR = 'samples' if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) X = tf.placeholder(dtype=tf.float32, shape=[None, HEIGHT, WIDTH, 3], name='X') Y = tf.placeholder(dtype=tf.float32, shape=[None, HEIGHT, WIDTH, 3], name='Y') k_initializer = tf.random_normal_initializer(0, 0.02) g_initializer = tf.random_normal_initializer(1, 0.02) def lrelu(x, leak=0.2): return tf.maximum(x, leak * x) def d_conv(inputs, filters, strides): padded = tf.pad(inputs, [[0, 0], [1, 1], [1, 1], [0, 0]], mode='CONSTANT') return tf.layers.conv2d(padded, kernel_size=4, filters=filters, strides=strides, padding='valid', kernel_initializer=k_initializer) def g_conv(inputs, filters): return tf.layers.conv2d(inputs, kernel_size=4, filters=filters, strides=2, padding='same', kernel_initializer=k_initializer) def g_deconv(inputs, filters): return tf.layers.conv2d_transpose(inputs, kernel_size=4, filters=filters, strides=2, padding='same', kernel_initializer=k_initializer) def batch_norm(inputs): return tf.layers.batch_normalization(inputs, axis=3, epsilon=1e-5, momentum=0.1, training=True, gamma_initializer=g_initializer) def sigmoid_cross_entropy_with_logits(x, y): return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, labels=y) 复制代码

判别器部分,将X和Y按通道拼接,经过多次卷积后得到 30*30*1 的判别图,即PatchGAN的思想,而之前是只有一个神经元的Dense

def discriminator(x, y, reuse=None): with tf.variable_scope('discriminator', reuse=reuse): x = tf.concat([x, y], axis=3) h0 = lrelu(d_conv(x, 64, 2)) # 128 128 64 h0 = d_conv(h0, 128, 2) h0 = lrelu(batch_norm(h0)) # 64 64 128 h0 = d_conv(h0, 256, 2) h0 = lrelu(batch_norm(h0)) # 32 32 256 h0 = d_conv(h0, 512, 1) h0 = lrelu(batch_norm(h0)) # 31 31 512 h0 = d_conv(h0, 1, 1) # 30 30 1 h0 = tf.nn.sigmoid(h0) return h0 复制代码

生成器部分,Unet前后两部分各包含8层卷积,且后半部分的前三层卷积使用Dropout,Dropout层在训练过程中以一定概率随机去掉一些神经元,起到防止过拟合的作用

def generator(x): with tf.variable_scope('generator', reuse=None): layers = [] h0 = g_conv(x, 64) layers.append(h0) for filters in [128, 256, 512, 512, 512, 512, 512]: h0 = lrelu(layers[-1]) h0 = g_conv(h0, filters) h0 = batch_norm(h0) layers.append(h0) encode_layers_num = len(layers) # 8 for i, filters in enumerate([512, 512, 512, 512, 256, 128, 64]): skip_layer = encode_layers_num - i - 1 if i == 0: inputs = layers[-1] else: inputs = tf.concat([layers[-1], layers[skip_layer]], axis=3) h0 = tf.nn.relu(inputs) h0 = g_deconv(h0, filters) h0 = batch_norm(h0) if i < 3: h0 = tf.nn.dropout(h0, keep_prob=0.5) layers.append(h0) inputs = tf.concat([layers[-1], layers[0]], axis=3) h0 = tf.nn.relu(inputs) h0 = g_deconv(h0, 3) h0 = tf.nn.tanh(h0, name='g') return h0 复制代码

损失函数,G加上L1损失

g = generator(X) d_real = discriminator(X, Y) d_fake = discriminator(X, g, reuse=True) vars_g = [var for var in tf.trainable_variables() if var.name.startswith('generator')] vars_d = [var for var in tf.trainable_variables() if var.name.startswith('discriminator')] loss_d_real = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_real, tf.ones_like(d_real))) loss_d_fake = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_fake, tf.zeros_like(d_fake))) loss_d = loss_d_real + loss_d_fake loss_g_gan = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_fake, tf.ones_like(d_fake))) loss_g_l1 = tf.reduce_mean(tf.abs(Y - g)) loss_g = loss_g_gan + loss_g_l1 * LAMBDA 复制代码

定义优化器

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): optimizer_d = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_d, var_list=vars_d) optimizer_g = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_g, var_list=vars_g) 复制代码

训练模型

sess = tf.Session() sess.run(tf.global_variables_initializer()) loss = {'d': [], 'g': []} for i in tqdm(range(100000)): k = i % X_all.shape[0] X_batch, Y_batch = X_all[k:k + batch_size, :, :, :], Y_all[k:k + batch_size, :, :, :] _, d_ls = sess.run([optimizer_d, loss_d], feed_dict={X: X_batch, Y: Y_batch}) _, g_ls = sess.run([optimizer_g, loss_g], feed_dict={X: X_batch, Y: Y_batch}) loss['d'].append(d_ls) loss['g'].append(g_ls) if i % 1000 == 0: print(i, d_ls, g_ls) gen_imgs = sess.run(g, feed_dict={X: X_batch}) result = np.zeros([HEIGHT, WIDTH * 3, 3]) result[:, :WIDTH, :] = (X_batch[0] + 1) / 2 result[:, WIDTH: 2 * WIDTH, :] = (Y_batch[0] + 1) / 2 result[:, 2 * WIDTH:, :] = (gen_imgs[0] + 1) / 2 plt.axis('off') plt.imshow(result) imsave(os.path.join(OUTPUT_DIR, 'sample_%d.jpg' % i), result) plt.show() plt.plot(loss['d'], label='Discriminator') plt.plot(loss['g'], label='Generator') plt.legend(loc='upper right') plt.savefig('Loss.png') plt.show() 复制代码

结果如下图所示,从左往右三张图依次为原图、真实图、生成图


深度有趣 | 19 pix2pix图像翻译

保存模型,以便在单机上使用

saver = tf.train.Saver() saver.save(sess, './pix2pix_diy', global_step=100000) 复制代码

在单机上加载模型,对val中的图片进行翻译

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np from imageio import imread, imsave import glob images = glob.glob('data/val/*.jpg') X_all = [] Y_all = [] WIDTH = 256 HEIGHT = 256 N = 10 images = np.random.choice(images, N, replace=False) for image in images: img = imread(image) img = (img / 255. - 0.5) * 2 # B2A X_all.append(img[:, WIDTH:, :]) Y_all.append(img[:, :WIDTH, :]) X_all = np.array(X_all) Y_all = np.array(Y_all) print(X_all.shape, Y_all.shape) sess = tf.Session() sess.run(tf.global_variables_initializer()) saver = tf.train.import_meta_graph('./pix2pix_diy-100000.meta') saver.restore(sess, tf.train.latest_checkpoint('./')) graph = tf.get_default_graph() g = graph.get_tensor_by_name('generator/g:0') X = graph.get_tensor_by_name('X:0') gen_imgs = sess.run(g, feed_dict={X: X_all}) result = np.zeros([N * HEIGHT, WIDTH * 3, 3]) for i in range(N): result[i * HEIGHT: i * HEIGHT + HEIGHT, :WIDTH, :] = (X_all[i] + 1) / 2 result[i * HEIGHT: i * HEIGHT + HEIGHT, WIDTH: 2 * WIDTH, :] = (Y_all[i] + 1) / 2 result[i * HEIGHT: i * HEIGHT + HEIGHT, 2 * WIDTH:, :] = (gen_imgs[i] + 1) / 2 imsave('facades翻译结果.jpg', result) 复制代码 造好的轮子

看一下项目提供了哪些造好的轮子, github.com/affinelayer…

将图片处理成 256*256 大小, input_dir 表示原始图片目录, output_dir 表示大小统一处理后的图片目录

python tools/process.py --input_dir input_dir --operation resize --output_dir output_dir 复制代码

准备好X和Y的配对数据(两个文件夹分别存放X和Y,对应图片的名称和尺寸相同),将图片像facades那样两两组合起来

python tools/process.py --input_dir X_dir --b_dir Y_dir --operation combine --output_dir combine_dir 复制代码

得到 combine_dir 之后即可训练配对图像pix2pix翻译模型

python pix2pix.py --mode train --output_dir model_dir --max_epochs 200 --input_dir combine_dir --which_direction AtoB 复制代码 mode :运行模式, train 表示训练模型 output_dir :模型输出路径 max_epochs :训练的轮数(epoch和iteration的区别) input_dir :组合图片路径 which_direction :翻译的方向,从左往右还是从右往左

模型训练过程中,以及模型训练完毕后,都可以使用tensorboard查看训练细节

tensorboard --logdir=model_dir 复制代码

训练完模型后,在测试数据上进行翻译

python pix2pix.py --mode test --output_dir output_dir --input_dir input_dir --checkpoint model_dir 复制代码 mode :运行模式, test 表示测试 output_dir :翻译结果输出路径 input_dir :待测试的图片路径 checkpoint :之前训练得到的模型路径

如果要训练上色模型,则不需要以上提到的组合图片这一步骤,只需要提供一个彩色图片文件夹即可,因为对应的灰度图可以从彩色图中自动抽取

python pix2pix.py --mode train --output_dir model_dir --max_epochs 200 --input_dir combine_dir --lab_colorization 复制代码

项目还提供了一些训练好的配对图像翻译模型

楼房:从标注到实景 街景:双向 地图:双向 鞋子:从边缘到实物 包:从边缘到实物 风景图片上色

使用以下数据集, lear.inrialpes.fr/~jegou/data… ,都是一些旅游风景照片,已经处理成 256*256 大小,分为train和test两部分,分别包含750和62张图片

使用train中的图片训练上色模型

python pix2pix.py --mode train --output_dir photos/model --max_epochs 200 --input_dir photos/data/train --lab_colorization 复制代码

使用test中的图片进行测试,模型会生成每一张彩色图对应的灰度图和上色图,并将全部上色结果写入一个网页中

python pix2pix.py --mode test --output_dir photos/test --input_dir photos/data/test --checkpoint photos/model 复制代码

上色结果如下,从左往右依次为灰度图、上色图、原图


深度有趣 | 19 pix2pix图像翻译

“日常研究”之动态刷新 respage01 百度热力图

$
0
0

因为这个DashBoard自己会一直开发维护下去,原本会演变成一个《试用 vue-admin-template 写一个自己的dashboard》+ 1、2、3、、N。但是今天还是把标题改掉了,因为其实重点并不是dashboard本身,真正的重点是我日常想去做的业余研究,顺便研究些华丽的前后端技术。

respage01 再说明

Respage01的起因是老婆大人对周边各种早教和教育培训机构春笋般出现的现象感到费解,然后向我提了一个问题,“如果我们入股类似的一个小教育培训机构,会不会有前途?”。这是一个很难回答的问题,因为我们都没有在培训教育行业里待过,但是既然问题已经出现了,自己也觉得这个问题有些意思,便想要做一个日常研究。Respage01会从宏观角度去分析并监测教育培训机构(在金华地区)相关的数据,数据都来自互联网。

预加功能

上一次已经留下了一个壳子,类似这样:


“日常研究”之动态刷新 respage01 百度热力图

但是从代码上也看到了,我是把某一天的坐标数据硬编码到了这个页面中,所以今天就是要来做成动态,刚好结合《部署Django REST Framework服务(Nginx + uWSGI + Django)》,将接口部署到线上。

既然做了接口化,那我们就可以定时做好每天的数据爬取 -> 数据清洗 -> 数据入库 -> 接口吐出。那么页面便可每天获取到最新的数据。

问题又来了,既然每天有了最新的数据,那最好是有方式可以看到每天的变化。可能在一定的时间跨度内,可以看到一些明显的变化,所以我打算做一个数据回看功能,做法可能很简单: 触发了某个按钮后,定时刷新所有的数据,或者做成一个短视频形式

在一个较短的时间跨度内,有可能很难从热力图上看到变化,所以打算加一个折线图来标识每天的数据。

部署自动爬取和数据清洗并入库

目前的爬虫目录很简单:

. ├── config.json ├── log │ └── train-2018-09-03.log ├── result │ └── train-2018-09-03.txt └── spiker.py 复制代码

那完成这个事情定时运行类似这样的脚本就可以:

python spiker.py | python format.py | python writeToRedis.py && python redisTomysql.py 复制代码 虽然有人会建议可以直接在爬取时完成清洗、去重和入库的动作,但是我还是喜欢用这种流式的方法来处理,这样更加清晰,功能也更加解耦。而且了解管道的同学可以看出来,这其实就是同时完成了爬取、清洗和入库动作,只不过是每条数据串行完成了这系列动作。这里的writeToRedis.py是为了利用redis天然的去重功能,redis的读写性能也会让效率更高些。 修改spiker.py

修改就只有两个:

将原先的查询关键字等配置信息写到config.json中,方便各管道节点获取到统一的信息 在原先写文件的地方,直接加个print,将数据标准输出。 """ 查询关键字:移到config.json """ FileKey = 'train' KeyWord = u"早教$培训" 复制代码 ## 设置标准输出的编码格式 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8') for r in res['results']: file.writelines(str(r).strip() + '\n') # 增加标准输出 print(str(r).strip(), flush=True) 复制代码 新增format.py 过滤无用信息

首先读取上一级管道的标准输出作为输入,使用fileinput便可实现:

def main(): for line in fileinput.input(): format(line) 复制代码

分析一条的数据的结构,只保留感兴趣的数据(不用担心丢失,因为在第一个节点上已经保存了原始数据),并尽量把json结构拉平成只有一级:

{'name': '英伦艺术培训', 'lat': 29.109614, 'lng': 119.662018, 'address': '解放东路238号福莲汇8层', 'province': '浙江省', 'city': '金华市', 'area': '婺城区', 'street_id': '15ca1ce6773a95f7a2a9343c', 'detail': 1, 'uid': '15ca1ce6773a95f7a2a9343c', 'detail_info': {'tag': '教育培训;培训机构', 'type': 'education', 'detail_url': 'http://api.map.baidu.com/place/detail?uid=15ca1ce6773a95f7a2a9343c&output=html&source=placeapi_v2', 'overall_rating': '0.0', 'children': []}} 复制代码

由于该数据一级比较简单,所以format也只是做了很小的处理,另外,这样的好处时,不同的数据结构可以写不同的format就可以。

# coding: utf-8 import fileinput import io import sys import chardet sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8') def format(line): """ :param line: :return: """ result = {} tmp = eval(line.decode('utf-8')) try: result = { "name": str(tmp["name"]), "lat": tmp["location"]["lat"], "lng": tmp["location"]["lng"], "address": str(tmp["address"]), "tag": str(tmp["detail_info"]["tag"]), } # 部分数据可能缺失字段 if "detail_url" in tmp["detail_info"]: result["detail_url"] = tmp["detail_info"]["detail_url"] else: result["detail_url"] = "" if "overall_rating" in tmp["detail_info"]: result["rate"] = tmp["detail_info"]["overall_rating"] else: result["rate"] = "0.0" print(str(result).strip(), flush=True) except Exception as e: print(e) pass def main(): try: for line in fileinput.input(mode='rb'): format(line) sys.stderr.close() except Exception as e: print(e) pass if __name__ == '__main__': main() 复制代码

如果数据量大,可以用类似的方法来调试:

cat node1.txt | head -n 1 | python format.py 复制代码

其实使用python的set也可以完成去重的事情,代码中也可以尝试这样的操作。关于去重的方式,在不同场景下有各式的方案,我们这属于简单场景,因为数据量不大。

系统安装redis服务,并配置密码:

sudo apt-get install redis-server 复制代码

在虚拟环境下安装redis库:

pip install redis vi /etc/redis/redis.conf # 打开 requirepass 配置项,并后面跟上密码 requirepass xxxx 复制代码

登录测试:

redis-cli -a xxxx 复制代码

redis有String、List、Set、Hash、Sort Hash几种类型,由于我们只是要做去重,那就用Set结构就可以:

train_2018_09_07(key) -> (数据1,数据2 ... 数据n) 复制代码

writeToRedis的简单实现:

# coding: utf-8 import fileinput import redis import time from tool.tool import tool import io import sys sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8') def connectRedis(): re = redis.Redis( host=tool.getRedisHost(), port=tool.getRedisPort(), password=tool.getRedisPass(), decode_responses=True) return re def main(): today = time.strftime("%Y_%m_%d") setName = tool.getFileKey() + "_" + today try: re = connectRedis() for line in fileinput.input(mode='rb'): re.sadd(setName, line.decode('utf-8').strip()) exit(0) except Exception as e: print(e) exit(-1) if __name__ == '__main__': main() 复制代码 使用redis的set还有一个好处就是,可以理由set的差集等功能,快速获取每天发生变化的数据。这个需求打算后面加上

执行:

python spiker.py | python format.py | python writeToRedis.py 复制代码

运行后,原始数据文件条目:

cat train-2018-09-07.txt | wc -l 663 复制代码

Redis Set 内的条目数:

127.0.0.1:6379> SCARD train_2018_09_07 (integer) 640 复制代码

说明确实还是有重复的数据,原因可能是我们使用了10*10小矩形扫描的方式,不可避免地会有交界处重叠的问题。

关于使用了redis后还是否需要Mysql的讨论也有很多,大家可以去参与讨论。我个人的考虑是Django上可以更好地支持Mysql来做序列化和反序列化,毕竟Mysql的查询功能也更加舒适一些。

首先从redis中读出数据的形式,可以使用迭代器的方式,好处是稍微省内存些,但是问题是如果单条数据单独写入mysql的话,IO上估计也不太合算,所以我使用pandas的DataFrame写入mysql的方式来分批写入,使用pandas的好处是字典数据写入数据操作比一般的mysql库要简洁很多。

虚拟环境下安装pandas:

pip install pandas sqlalchemy 复制代码 # coding: utf-8 import redis from tool.tool import tool import time import pandas as pd from sqlalchemy import create_engine import pymysql def connectRedis(): """ 连接Redis :return: redis connect """ re = redis.Redis( host=tool.getRedisHost(), port=tool.getRedisPort(), password=tool.getRedisPass(), decode_responses=True) return re def connectMysql(): """ 连接mysql数据库 :return: engine connect """ config = tool.getMysqlConfig() engine = create_engine(str(r"mysql+pymysql://%s:%s@%s/%s?charset=utf8") % (config['User'], config['Pass'], config['Host'], config['Name'])) return engine def redisToMysql(re, en): """ :param re: redis connect :param en: mysql engine connect :return: """ today = time.strftime("%Y_%m_%d") tableName = tool.getFileKey() + '_' + today res = [] index = 0 for item in re.sscan_iter(tool.getFileKey() + '_' + today): tmp = eval(item.encode('utf-8').decode('utf-8')) tmp['time'] = today res.append(tmp) index += 1 if index >= 100: df = pd.DataFrame(res) df.to_sql('respage01', con=en, if_exists='append', index=False,) index = 0 res = [] if index != 0: df = pd.DataFrame(res) df.to_sql(name='respage01', con=en, if_exists='append', index=False) # 添加主键 # print("xxxxxxxx") # with en.connect() as con: # con.execute("alter table respage01 add COLUMN id INT NOT NULL AUTO_INCREMENT primary key first") def main(): re = connectRedis() en = connectMysql() redisToMysql(re, en) if __name__ == '__main__': main() 复制代码

为了后面django处理方便,我后面临时加入了一个自增id作为主键。方法可以是:

alter table respage01 add COLUMN id INT NOT NULL AUTO_INCREMENT primary key first; 复制代码 编写相关apis

我们设计两个api: * 获取某个时间段内的所有坐标数据 * 获取某个时间段内每天的数量值

model实现 class Respage01Info(models.Model): """ respage 01 相关的数据 """ time = models.CharField(max_length=100) name = models.CharField(max_length=200) address = models.CharField(max_length=500) detail_url = models.URLField(max_length=500) rate = models.FloatField() lat = models.FloatField() lng = models.FloatField() class Meta: # 指定数据表 db_table = "respage01" 复制代码

需要注意的是,我们已经拥有了数据库,并且表里已经有了数据,所以在执行migrate的时候,需要指明fake掉该项目的数据迁移:

python manage.py migrate --fake rouboapi 复制代码 Serializer实现

由于我们的计数接口是需要使用聚合类查询功能,简单说,就是需要返回数据库字段以外的字段给客户端,所以需要使用serializers的Field方法。

class Respage01Serializer(serializers.HyperlinkedModelSerializer): """ 序列化Respage01相关的数据 """ class Meta: model = Respage01Info fields = ('time', 'lat', 'lng', 'name', 'address', 'detail_url', 'rate') class Respage01CountSerializer(serializers.HyperlinkedModelSerializer): """ 序列化计数数据,用于序列化聚合类查询的结果 """ time = serializers.StringRelatedField() count = serializers.IntegerField() class Meta: model = Respage01Info fields = ('time', 'count') 复制代码 view实现

这里需要用到django的数据库查询相关的知识,我们这里用到了fiter、values、annotate几个函数,具体的可以参考官方文档,基本用法还是比较简单。

class Respage01(APIView): """ 获取respage01相关的数据 """ authentication_classes = [] permission_classes = [] def rangeTime(self, start_time, end_time): """ 获取时间区间 :param start_time: :param end_time: :return: """ print("------------") dateList = [datetime.strftime(x, "%Y_%m_%d") for x in list(pd.date_range(start=start_time.replace('_',''), end=end_time.replace('_','')))] return dateList def get(self, request, format=None): req = request.query_params if 'type' not in req or 'start_time' not in req or 'end_time' not in req: return Response({}, status=status.HTTP_400_BAD_REQUEST) if req['type'] == 'location': dateList = self.rangeTime(start_time=req['start_time'], end_time=req['end_time']) queryset = Respage01Info.objects.filter(time__in=dateList) serializer = Respage01Serializer(queryset, many=True) elif req['type'] == 'count': dateList = self.rangeTime(start_time=req['start_time'], end_time=req['end_time']) queryset = Respage01Info.objects.filter(time__in=dateList).values('time').annotate(count=Count('id')) serializer = Respage01CountSerializer(queryset, many=True) return Response(serializer.data, status=status.HTTP_200_OK) 复制代码 接口上线后发现的异常

在接口上线后测试过程中,发现接口极其不稳定,查了一下发现mysql会异常地退出,查看了日志发现是内存不足导致。

我的vps是1G内存的基础配置,虽然小,但是不至于这么紧张。通过top【M】排序后惊奇地发现uwsgi开了10个进程,每个进程占用了7%左右的内存。修改uwsgi ini文件重启后故障排除(我们这种小服务,两个进程足够了)。

# mysite_uwsgi.ini file [uwsgi] # Django-related settings # the base directory (full path) chdir = /data/django/rouboApi # Django's wsgi file module = rouboinfo.wsgi # the virtualenv (full path) home = /data/django/env3 # process-related settings # master master = true # maximum number of worker processes processes = 2 # the socket (use the full path to be safe socket = /data/django/rouboApi/rouboapi.scok # ... with appropriate permissions - may be needed chmod-socket = 666 # clear environment on exit vacuum = true 复制代码 修改respage01页面

roubo’s dashboard 主要是增加了两个接口请求,并将v-charts的数据动态化。这里也简单加了一个“复盘”按钮,定时刷新数据,可以大概看到一些变化。

<template> <div> <div style="height: 100%"> <button @click="onPlay">复盘</button> <ve-heatmap :data="chartDataMap" :settings="chartSettingsMap" height="600px"/> </div> <div> <ve-line :data="chartDataChart" :settings="chartSettingsChart"/> </div> </div> </template> 复制代码 /** * 获取某个时间区间的位置信息 * @param start_time * @param end_time */ getLocations: function(start_time, end_time) { this.rouboapis.getRespage01Info('location', start_time, end_time, { success: (res) => { this.chartDataMap.rows = res }, fail: (err) => { console.log(err) } }) }, /** * 获取某个时间段的统计数据 * @param start_time * @param end_time */ getCount: function(start_time, end_time) { this.rouboapis.getRespage01Info('count', start_time, end_time, { success: (res) => { this.chartDataChart.rows = res } }) }, /** * 点击复盘按钮事件 */ onPlay: function() { const dateList = this.getDateList('2018_09_13', this.today('_')) let index = 0 const timer = setInterval(() => { this.getLocations(dateList[index], dateList[index]) this.getCount('2018_09_13', dateList[index]) index = index + 1 if (index >= dateList.length) { clearInterval(timer) return } }, 5000) } 复制代码

Matlab R2018b Python array passing

$
0
0

Matlab R2018b added additional bidirectional array passing with python, from Matlab. However, the mechanism used is Python memoryview it’s not just a Numpy.ndarray. Since Matlab R2014b, Matlab couldtransfer 1-D vectors with Python, but using memory copies which are slow for large amounts of data.

This has the potential to greatly speed up data transfer Matlab Python. However, one will have to use Python memoryview objects to take advantage of this. This is a step in the right direction by Mathworks, but it seems a bit more work is needed, as we feel working directly and efficiently (without copying) should be a basic feature between Matlab and Numpy ndarray .

深度有趣 | 20 CycleGAN性别转换

$
0
0

介绍可用于实现多种非配对图像翻译任务的CycleGAN模型,并完成性别转换任务

原理

和pix2pix不同,CycleGAN不需要严格配对的图片,只需要两类(domain)即可,例如一个文件夹都是苹果图片,另一个文件夹都是橘子图片

使用A和B两类图片,就可以实现A到B的翻译和B到A的翻译

论文官方网站上提供了详细的例子和介绍, junyanz.github.io/CycleGAN/ ,例如苹果和橘子、马和斑马、夏天和冬天、照片和艺术作品等


深度有趣 | 20 CycleGAN性别转换

以及论文的官方Github项目, github.com/junyanz/Cyc… ,使用PyTorch实现

CycleGAN由两个生成器G和F,以及两个判别器Dx和Dy组成


深度有趣 | 20 CycleGAN性别转换

G接受真的X并输出假的Y,即完成X到Y的翻译;F接受真的Y并输出假的X,即完成Y到X的翻译;Dx接受真假X并进行判别,Dy接受真假Y并进行判别

CycleGAN的损失函数和标准GAN差不多,只是写两套而已

除此之外,为了避免mode collasp问题,CycleGAN还考虑了循环一致损失(Cycle Consistency Loss)

因此CycleGAN的总损失如下,G、F、Dx、Dy分别需要min、max其中的部分损失项

实现

在论文的具体实现中,使用了两个tricks

使用Least-Square Loss即最小平方误差代替标准的GAN损失 以G为例,维护一个历史假Y图片集合,例如50张。每次G生成假Y之后将其加到集合中,再从集合中随机地取出一张假Y,和一张真Y一起输入给判别器进行判别。这样一来,假Y集合代表了G根据X生成Y的平均能力,使得训练更加稳定

使用以下项目训练CycleGAN模型, github.com/vanhuyz/Cyc… ,主要包括几个代码:

build_data.py :将图片数据整理为tfrecords文件 ops.py :定义了一些小的网络模块 generator.py :生成器的定义 discriminator.py :判别器的定义 model.py :使用生成器和判别器定义CycleGAN train.py :训练模型的代码 export_graph.py :将训练好的模型打包成 .pd 文件 inference.py :使用打包好的 .pb 文件翻译图片,即使用模型进行推断

生成器和判别器结构如下,如果感兴趣可以进一步阅读项目源码


深度有趣 | 20 CycleGAN性别转换
性别转换

使用CelebA中的男性图片和女性图片,训练一个实现性别转换的CycleGAN

将CelebA数据集中的图片处理成 256*256 大小,并按照性别保存至male和female两个文件夹,分别包含84434张男性图片和118165张女性图片

# -*- coding: utf-8 -*- from imageio import imread, imsave import cv2 import glob, os from tqdm import tqdm data_dir = 'data' male_dir = 'data/male' female_dir = 'data/female' if not os.path.exists(data_dir): os.mkdir(data_dir) if not os.path.exists(male_dir): os.mkdir(male_dir) if not os.path.exists(female_dir): os.mkdir(female_dir) WIDTH = 256 HEIGHT = 256 def read_process_save(read_path, save_path): image = imread(read_path) h = image.shape[0] w = image.shape[1] if h > w: image = image[h // 2 - w // 2: h // 2 + w // 2, :, :] else: image = image[:, w // 2 - h // 2: w // 2 + h // 2, :] image = cv2.resize(image, (WIDTH, HEIGHT)) imsave(save_path, image) target = 'Male' with open('list_attr_celeba.txt', 'r') as fr: lines = fr.readlines() all_tags = lines[0].strip('\n').split() for i in tqdm(range(1, len(lines))): line = lines[i].strip('\n').split() if int(line[all_tags.index(target) + 1]) == 1: read_process_save(os.path.join('celeba', line[0]), os.path.join(male_dir, line[0])) # 男 else: read_process_save(os.path.join('celeba', line[0]), os.path.join(female_dir, line[0])) # 女 复制代码

使用 build_data.py 将图片转换成tfrecords格式

python CycleGAN-TensorFlow/build_data.py --X_input_dir data/male/ --Y_input_dir data/female/ --X_output_file data/male.tfrecords --Y_output_file data/female.tfrecords 复制代码

使用 train.py 训练CycleGAN模型

python CycleGAN-TensorFlow/train.py --X data/male.tfrecords --Y data/female.tfrecords --image_size 256 复制代码

训练开始后,会生成checkpoints文件夹,并根据当前日期和时间生成一个子文件夹,例如 20180507-0231 ,其中包括用于显示tensorboard的 events.out.tfevents 文件,以及和模型相关的一些文件

使用tensorboard查看模型训练细节,运行以下命令后访问6006端口即可

tensorboard --logdir=checkpoints/20180507-0231 复制代码

以下是迭代185870次之后,tensorboard的IMAGES页面


深度有趣 | 20 CycleGAN性别转换

模型训练没有迭代次数限制,所以感觉效果不错或者迭代次数差不多了,便可以终止训练

使用 export_graph.py 将模型打包成 .pb 文件,生成的文件在pretrained文件夹中

python CycleGAN-TensorFlow/export_graph.py --checkpoint_dir checkpoints/20180507-0231/ --XtoY_model male2female.pb --YtoX_model female2male.pb --image_size 256 复制代码

通过 inference.py 使用模型处理图片

python CycleGAN-TensorFlow/inference.py --model pretrained/male2female.pb --input Trump.jpg --output Trump_female.jpg --image_size 256 复制代码 python CycleGAN-TensorFlow/inference.py --model pretrained/female2male.pb --input Hillary.jpg --output Hillary_male.jpg --image_size 256 复制代码

在代码中使用模型处理多张图片

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np from model import CycleGAN from imageio import imread, imsave import glob import os image_file = 'face.jpg' W = 256 result = np.zeros((4 * W, 5 * W, 3)) for gender in ['male', 'female']: if gender == 'male': images = glob.glob('../faces/male/*.jpg') model = '../pretrained/male2female.pb' r = 0 else: images = glob.glob('../faces/female/*.jpg') model = '../pretrained/female2male.pb' r = 2 graph = tf.Graph() with graph.as_default(): graph_def = tf.GraphDef() with tf.gfile.FastGFile(model, 'rb') as model_file: graph_def.ParseFromString(model_file.read()) tf.import_graph_def(graph_def, name='') with tf.Session(graph=graph) as sess: input_tensor = graph.get_tensor_by_name('input_image:0') output_tensor = graph.get_tensor_by_name('output_image:0') for i, image in enumerate(images): image = imread(image) output = sess.run(output_tensor, feed_dict={input_tensor: image}) with open(image_file, 'wb') as f: f.write(output) output = imread(image_file) maxv = np.max(output) minv = np.min(output) output = ((output - minv) / (maxv - minv) * 255).astype(np.uint8) result[r * W: (r + 1) * W, i * W: (i + 1) * W, :] = image result[(r + 1) * W: (r + 2) * W, i * W: (i + 1) * W, :] = output os.remove(image_file) imsave('CycleGAN性别转换结果.jpg', result) 复制代码
深度有趣 | 20 CycleGAN性别转换
视频性别转换

对一段视频,识别每一帧可能包含的人脸,检测人脸对应的性别,并使用CycleGAN完成性别的双向转换

使用以下项目实现性别的检测, github.com/yu4u/age-ge… ,通过Keras训练模型,可以检测出人脸的性别和年龄

举个例子,使用OpenCV获取摄像头图片,通过dlib检测人脸,并得到每一个检测结果对应的年龄和性别

# -*- coding: utf-8 -*- from wide_resnet import WideResNet import numpy as np import cv2 import dlib depth = 16 width = 8 img_size = 64 model = WideResNet(img_size, depth=depth, k=width)() model.load_weights('weights.hdf5') def draw_label(image, point, label, font=cv2.FONT_HERSHEY_SIMPLEX, font_scale=1, thickness=2): size = cv2.getTextSize(label, font, font_scale, thickness)[0] x, y = point cv2.rectangle(image, (x, y - size[1]), (x + size[0], y), (255, 0, 0), cv2.FILLED) cv2.putText(image, label, point, font, font_scale, (255, 255, 255), thickness) detector = dlib.get_frontal_face_detector() cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while True: ret, image_np = cap.read() image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB) img_h = image_np.shape[0] img_w = image_np.shape[1] detected = detector(image_np, 1) faces = [] if len(detected) > 0: for i, d in enumerate(detected): x0, y0, x1, y1, w, h = d.left(), d.top(), d.right(), d.bottom(), d.width(), d.height() cv2.rectangle(image_np, (x0, y0), (x1, y1), (255, 0, 0), 2) x0 = max(int(x0 - 0.25 * w), 0) y0 = max(int(y0 - 0.45 * h), 0) x1 = min(int(x1 + 0.25 * w), img_w - 1) y1 = min(int(y1 + 0.05 * h), img_h - 1) w = x1 - x0 h = y1 - y0 if w > h: x0 = x0 + w // 2 - h // 2 w = h x1 = x0 + w else: y0 = y0 + h // 2 - w // 2 h = w y1 = y0 + h faces.append(cv2.resize(image_np[y0: y1, x0: x1, :], (img_size, img_size))) faces = np.array(faces) results = model.predict(faces) predicted_genders = results[0] ages = np.arange(0, 101).reshape(101, 1) predicted_ages = results[1].dot(ages).flatten() for i, d in enumerate(detected): label = '{}, {}'.format(int(predicted_ages[i]), 'F' if predicted_genders[i][0] > 0.5 else 'M') draw_label(image_np, (d.left(), d.top()), label) cv2.imshow('gender and age', cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)) if cv2.waitKey(25) & 0xFF == ord('q'): cap.release() cv2.destroyAllwindows() break 复制代码

将以上项目和CycleGAN应用于视频的双向性别转换,首先提取出视频中的人脸,记录人脸出现的帧数、位置以及对应的性别,视频共830帧,检测出721张人脸

# -*- coding: utf-8 -*- from wide_resnet import WideResNet import numpy as np import cv2 import dlib import pickle depth = 16 width = 8 img_size = 64 model = WideResNet(img_size, depth=depth, k=width)() model.load_weights('weights.hdf5') detector = dlib.get_frontal_face_detector() cap = cv2.VideoCapture('../friends.mp4') pos = [] frame_id = -1 while cap.isOpened(): ret, image_np = cap.read() frame_id += 1 if len((np.array(image_np)).shape) == 0: break image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB) img_h = image_np.shape[0] img_w = image_np.shape[1] detected = detector(image_np, 1) if len(detected) > 0: for d in detected: x0, y0, x1, y1, w, h = d.left(), d.top(), d.right(), d.bottom(), d.width(), d.height() x0 = max(int(x0 - 0.25 * w), 0) y0 = max(int(y0 - 0.45 * h), 0) x1 = min(int(x1 + 0.25 * w), img_w - 1) y1 = min(int(y1 + 0.05 * h), img_h - 1) w = x1 - x0 h = y1 - y0 if w > h: x0 = x0 + w // 2 - h // 2 w = h x1 = x0 + w else: y0 = y0 + h // 2 - w // 2 h = w y1 = y0 + h face = cv2.resize(image_np[y0: y1, x0: x1, :], (img_size, img_size)) result = model.predict(np.array([face])) pred_gender = result[0][0][0] if pred_gender > 0.5: pos.append([frame_id, y0, y1, x0, x1, h, w, 'F']) else: pos.append([frame_id, y0, y1, x0, x1, h, w, 'M']) print(frame_id + 1, len(pos)) with open('../pos.pkl', 'wb') as fw: pickle.dump(pos, fw) cap.release() cv2.destroyAllWindows() 复制代码

再使用CycleGAN,将原视频中出现的人脸转换成相反的性别,并写入新的视频文件

# -*- coding: utf-8 -*- import tensorflow as tf import numpy as np from model import CycleGAN from imageio import imread import os import cv2 import pickle from tqdm import tqdm with open('../pos.pkl', 'rb') as fr: pos = pickle.load(fr) cap = cv2.VideoCapture('../friends.mp4') ret, image_np = cap.read() out = cv2.VideoWriter('../output.mp4', -1, cap.get(cv2.CAP_PROP_FPS), (image_np.shape[1], image_np.shape[0])) frames = [] while cap.isOpened(): ret, image_np = cap.read() if len((np.array(image_np)).shape) == 0: break frames.append(cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)) image_size = 256 image_file = 'face.jpg' for gender in ['M', 'F']: if gender == 'M': model = '../pretrained/male2female.pb' else: model = '../pretrained/female2male.pb' graph = tf.Graph() with graph.as_default(): graph_def = tf.GraphDef() with tf.gfile.FastGFile(model, 'rb') as model_file: graph_def.ParseFromString(model_file.read()) tf.import_graph_def(graph_def, name='') with tf.Session(graph=graph) as sess: input_tensor = graph.get_tensor_by_name('input_image:0') output_tensor = graph.get_tensor_by_name('output_image:0') for i in tqdm(range(len(pos))): fid, y0, y1, x0, x1, h, w, g = pos[i] if g == gender: face = cv2.resize(frames[fid - 1][y0: y1, x0: x1, :], (image_size, image_size)) output_face = sess.run(output_tensor, feed_dict={input_tensor: face}) with open(image_file, 'wb') as f: f.write(output_face) output_face = imread(image_file) maxv = np.max(output_face) minv = np.min(output_face) output_face = ((output_face - minv) / (maxv - minv) * 255).astype(np.uint8) output_face = cv2.resize(output_face, (w, h)) frames[fid - 1][y0: y1, x0: x1, :] = output_face for frame in frames: out.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)) os.remove(image_file) cap.release() out.release() cv2.destroyAllWindows() 复制代码

生成的视频文件只有图像、没有声音,可以使用 ffmpeg 进一步处理

如果没有 ffmpeg 则下载并安装, www.ffmpeg.org/download.ht…

进入命令行,从原始视频中提取音频

ffmpeg -i friends.mp4 -f mp3 -vn sound.mp3 复制代码

将提取的音频和生成的视频合成在一起

ffmpeg -i output.mp4 -i sound.mp3 combine.mp4 复制代码 其他

项目还提供了四个训练好的模型, github.com/vanhuyz/Cyc… ,包括苹果到橘子、橘子到苹果、马到斑马、斑马到马,如果感兴趣可以尝试一下

用CycleGAN不仅可以完成两类图片之间的转换,也可以实现两个物体之间的转换,例如将一个人翻译成另一个人

可以考虑从一部电影中提取出两个角色对应的图片,训练CycleGAN之后,即可将一个人翻译成另一个人

还有一些比较大胆的尝试, 提高驾驶技术:用GAN去除(爱情)动作片中的马赛克和衣服


Win7环境下VS2015+Python 2.7 编译face_recognition

$
0
0

最近在研究一个人脸识别开源框架 face_recognition ,编译需要依赖 dlib、boost、cmake 等相关环境,在编译的时候,踩了一大堆坑,网上资料大都不是很全面再加上 windows 环境下去配置这些本身就比 Liunx 或 Mac下配置要相对麻烦一些。如果你是准备在 Windows 环境下编译,就做好准备踩坑吧~~~

系统环境

Windows 7 python 2.7.14 VS2015 安装步骤

1. 首先 github.com/davisking/d… 下载整个zip

git clone https://github.com/davisking/dlib](https://github.com/davisking/dlib 复制代码 2. 前置的一些python库要安装, scipy, numpy+mkl 这两个用pip安装可能会蛋疼, www.lfd.uci.edu/~gohlke/pyt… 到这里面找对应版本的wheel, 然后用easy_install就KO了 3. 安装Boost sourceforge.net/projects/bo… 下载boost-binaries, 最新的,直接点击exe程序等待安装完毕, 正常的话安装的目录是X:\local\boost_1_XX_X(保持版本名一致, 也就是XX_X别改) 4. 这一步也貌似可以不用 系统变量加上 VS2015的位置 新建一个 VS140COMNTOOLS 值 X:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\ 复制代码 5. 去到Boost下面, 双击一下bootstrap.bat, 运行OK之后boost_1_66_0\tools\build文件夹下找到以下两个文件
Win7环境下VS2015+Python 2.7 编译face_recognition

然后开启一个命令行,定位到这个文件夹,运行命令:

b2 install 复制代码

运行完之后再执行下面命令:

b2 -a --with-python address-model=64 toolset=msvc runtime-link=static 复制代码

成功后能找到 stage 文件夹

6.在系统环境配置好下面两个环境变量 BOOST_ROOT=C:\local\boost_X_XX_X BOOST_LIBRARYDIR=C:\local\boost_X_XX_X\stage\lib 复制代码

最后执行以下命令:

python setup.py install --yes USE_AVX_INSTRUCTIONS --yes DLIB_USE_CUDA 复制代码

CUDA这个, 主要是用在有显卡的机器学习, 如果没有可以不加 完成! 打开python shell 试一下 import dlib 没问题就可以

pip install face_recognition 复制代码

安装成功之后,我们可以在python中正常 import face_recognition 了


Win7环境下VS2015+Python 2.7 编译face_recognition
编写人脸检测程序

此demo主要展示了识别指定图片中人脸的特征数据,下面就是人脸的八个特征,我们就是要获取特征数据

'chin', 'left_eyebrow', 'right_eyebrow', 'nose_bridge', 'nose_tip', 'left_eye', 'right_eye', 'top_lip', 'bottom_lip' 复制代码 人脸检测代码 # -*- coding: utf-8 -*- # 自动识别人脸特征 # filename : find_facial_features_in_picture.py # 导入pil模块 ,可用命令安装 apt-get install python-Imaging from PIL import Image, ImageDraw # 导入face_recogntion模块,可用命令安装 pip install face_recognition import face_recognition # 将jpg文件加载到numpy 数组中 image = face_recognition.load_image_file("mayun.jpg") #查找图像中所有面部的所有面部特征 face_landmarks_list = face_recognition.face_landmarks(image) print("I found {} face(s) in this photograph.".format(len(face_landmarks_list))) for face_landmarks in face_landmarks_list: #打印此图像中每个面部特征的位置 facial_features = [ 'chin', 'left_eyebrow', 'right_eyebrow', 'nose_bridge', 'nose_tip', 'left_eye', 'right_eye', 'top_lip', 'bottom_lip' ] for facial_feature in facial_features: print("The {} in this face has the following points: {}".format(facial_feature, face_landmarks[facial_feature])) #让我们在图像中描绘出每个人脸特征! pil_image = Image.fromarray(image) d = ImageDraw.Draw(pil_image) for facial_feature in facial_features: d.line(face_landmarks[facial_feature], width=5) pil_image.show() 复制代码

运行结果: 自动识别图片中的人脸,并且识别它的特征

原图:
Win7环境下VS2015+Python 2.7 编译face_recognition
运行效果:
Win7环境下VS2015+Python 2.7 编译face_recognition

####编写人脸识别程序

注意:这里使用了 python-opencv,一定要配置好了opencv才能运行成功。 opencv选择跟自己python版本相匹配的版本,可以在这个网站( www.lfd.uci.edu/~gohlke/pyt… .whl(我的python版本是2.7所以选择该版本安装),安装完成之后,打开 cmd 输入 import cv2 没有提示任何错误说明安装成功。


Win7环境下VS2015+Python 2.7 编译face_recognition
# -*- coding: utf-8 -*- # # 检测人脸 import face_recognition import cv2 # 读取图片并识别人脸 img = face_recognition.load_image_file("mayun.jpeg") face_locations = face_recognition.face_locations(img) print face_locations # 调用opencv函数显示图片 img = cv2.imread("mayun.jpeg") cv2.namedWindow("原图") cv2.imshow("原图", img) # 遍历每个人脸,并标注 faceNum = len(face_locations) for i in range(0, faceNum): top = face_locations[i][0] right = face_locations[i][1] bottom = face_locations[i][2] left = face_locations[i][3] start = (left, top) end = (right, bottom) color = (55,255,155) thickness = 3 cv2.rectangle(img, start, end, color, thickness) # 显示识别结果 cv2.namedWindow("人脸识别") cv2.imshow("人脸识别", img) cv2.waitKey(0) cv2.destroyAllWindows() 复制代码

运行结果: 程序会读取当前目录下指定的图片,然后识别其中的人脸,并标注每个人脸。


Win7环境下VS2015+Python 2.7 编译face_recognition
摄像头实时识别人脸

此处因为公司台式机没有摄像头,所以用的Mac上运行这个demo,配置都是差不多的,环境配置好,运行下面代码即可

# -*- coding: utf-8 -*- # 摄像头实时识别人脸 import face_recognition import cv2 video_capture = cv2.VideoCapture(0) # 加载本地图片 xhb_img = face_recognition.load_image_file("xhb.jpg") xhb_face_encoding = face_recognition.face_encodings(xhb_img)[0] # 初始化变量 face_locations = [] face_encodings = [] face_names = [] process_this_frame = True while True: ret, frame = video_capture.read() small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25) if process_this_frame: face_locations = face_recognition.face_locations(small_frame) face_encodings = face_recognition.face_encodings(small_frame, face_locations) face_names = [] for face_encoding in face_encodings: # 可以自定义设置识别阈值(tolerance)此处设置为0.5,默认为0.6,太小可能识别不出来,太大可能造成识别混淆 match = face_recognition.compare_faces([xhb_face_encoding], face_encoding,tolerance=0.5) if match[0]: name = "xiaohaibin" else: name = "unknown" face_names.append(name) process_this_frame = not process_this_frame for (top, right, bottom, left), name in zip(face_locations, face_names): top *= 4 right *= 4 bottom *= 4 left *= 4 cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2) cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), 2) font = cv2.FONT_HERSHEY_DUPLEX cv2.putText(frame, name, (left+6, bottom-6), font, 1.0, (255, 255, 255), 1) cv2.imshow('Video', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break video_capture.release() cv2.destroyAllWindows() 复制代码

运行结果展示


Win7环境下VS2015+Python 2.7 编译face_recognition
相关资料 face_recognition OpenCv Boost C++ Libraries

原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文

$
0
0
0x00 前言

之前看到一篇文章是关于TPYBoard v102控制OLED屏显示的,看到之后就想尝试一下使用OLED屏来显示中文。最近利用空余时间搞定了这个实验,特此将实验过程及源码分享出来,方便以后使用。

0x01 实验器材

TPYBoard v102 开发板 1块(某宝上可以买到,价格不贵)

0.96 寸OLED显示屏(ssd1306) 1块

杜邦线 若干

0x02 前期准备

1、首先我们先来看一下,之前参考的OLED显示字符的文章。

http://docs.tpyboard.com/zh/latest/tpyboard/tutorial/v10x/oled/?highlight=oled

文章中的源码文件都已上传到GitHub。地址: https://github.com/TPYBoard/developmentBoard/tree/master/TPYBoard-v10x-master

找到11.学习使用OLED显示屏]里面就是源程序。我就是在font.py和ssd1306.py基础上做的开发。

2、在font.py中增加中文字模。

font.py中已有英文、数字和符号的字符,我们需要做中文的字模添加到font.py中。

2.1首先下载字模提取工具。地址: http://tpyboard.com/download/tool/187.html

解压,双击运行PCtoLCD2002.exe。


原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文
2.2 顶端菜单栏,点击[选项]按下方图片设置,设置完毕后点击[确定]保存设置。
原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文
2.2回到主界面,在输入框中输入“我”点击[生成字模]。
原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文
取得的字模数据如下:
原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文
2.2将取到的字模数据添加到font.py中。
原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文
绿色框中是“我”的16进制utf-8编码。

在线工具: http://tool.lu/hexstr/

参照以上方法,我依次添加了“我爱你祖国”这5个汉字的字模。

byte2 = {
0xe68891:
[
0x04,0x0E,0x78,0x08,0x08,0xFF,0x08,0x08,0x0A,0x0C,0x18,0x68,0x08,0x08,0x2B,0x10,
0x40,0x50,0x48,0x48,0x40,0xFE,0x40,0x44,0x44,0x48,0x30,0x22,0x52,0x8A,0x06,0x02,
],#我
0xe788b1:
[
0x00,0x01,0x7E,0x22,0x11,0x7F,0x42,0x82,0x7F,0x04,0x07,0x0A,0x11,0x20,0x43,0x1C,
0x08,0xFC,0x10,0x10,0x20,0xFE,0x02,0x04,0xF8,0x00,0xF0,0x10,0x20,0xC0,0x30,0x0E,
],#爱
0xe4bda0:
[
0x08,0x08,0x08,0x11,0x11,0x32,0x34,0x50,0x91,0x11,0x12,0x12,0x14,0x10,0x10,0x10,
0x80,0x80,0x80,0xFE,0x02,0x04,0x20,0x20,0x28,0x24,0x24,0x22,0x22,0x20,0xA0,0x40,
],#你
0xe7a596:
[
0x20,0x11,0x11,0xF9,0x09,0x11,0x11,0x39,0x55,0x95,0x11,0x11,0x11,0x11,0x17,0x10,
0x00,0xF8,0x08,0x08,0x08,0xF8,0x08,0x08,0x08,0xF8,0x08,0x08,0x08,0x08,0xFE,0x00
],#祖
0xe59bbd:
[
0x00,0x7F,0x40,0x40,0x5F,0x41,0x41,0x4F,0x41,0x41,0x41,0x5F,0x40,0x40,0x7F,0x40,
0x00,0xFC,0x04,0x04,0xF4,0x04,0x04,0xE4,0x04,0x44,0x24,0xF4,0x04,0x04,0xFC,0x04
],#国
}

1、在ssd1306.py 文件中增加了draw_chinese显示中文的方法。

def draw_chinese(self,ch_str,x_axis,y_axis):
offset_=0
y_axis=y_axis*8#中文高度一行占8个
x_axis=127-(x_axis*16)#中文宽度占16个
for k in ch_str:
code = 0x00#将中文转成16进制编码
data_code = k.encode("utf-8")
code |= data_code[0]<<16
code |= data_code[1]<<8
code |= data_code[2]
byte_data=font.byte2[code]
for y in range(0,16):
a_=bin(byte_data[y]).replace('0b','')
while len(a_)<8:
a_='0'+a_
b_=bin(byte_data[y+16]).replace('0b','')
while len(b_)<8:
b_='0'+b_
for x in range(0,8):
self.set_pixel(x_axis-x-offset_,y+y_axis,int(a_[x]))#文字的上半部分
self.set_pixel(x_axis-x-8-offset_,y+y_axis,int(b_[x]))#文字的下半部分
offset_+=16

github源码地址:https://github.com/TPYBoard/developmentBoard/tree/master/TPYBoard-v10x-master/

找到[20.学习OLED显示中文]。

0x03 硬件连接

本次实验使用OLED的SPI通讯方式,TPYBoard v102带有2个SPI接口,我用的SPI1。


原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文
具体接线方法如下: 0x04 效果展示

硬件接线OK后,将源码全部拷贝到TPYBaord v102加载的磁盘中,按下RST按键复位或者使用Putty软件Ctrl+D软复位,重新运行效果如下:


原 荐 MicroPython实例之TPYBoard开发板控制OLED显示中文

Django 1&period;10 Delete the cascading cascade query

$
0
0

I'm trying to delete all objects from a large queryset. Here is my models.py

from __future__ import unicode_literals from django.contrib.auth.models import User from django.db import models class Fund(models.Model): name = models.CharField(max_length=255, blank=False, null=False) start_date = models.DateField(default=None, blank=False, null=False) def __unicode__(self): return self.name class FundData(models.Model): fund = models.ForeignKey(Fund, on_delete=models.CASCADE) date = models.DateField(default=None, blank=False, null=False) value = models.FloatField(default=None, blank=True, null=True) def __unicode__(self): return "{} --- Data: {} --- Value: {} ".format(str(self.fund), str(self.date), str(self.value))

But when I try to delete all records the query take too much time and mysql is being timed out.

Fund.objects.all().delete() What is the best way to manage this operation inside a view? There is a way to do that calling a django command from terminal?

First of all you can change the timeout time of MySQL by edditing settings.py

DATABASES = { 'default': { ... OPTIONS = { 'connect_timeout': 5, # your timeout time } ... } }

The reasons why .delete() may be slow are:

Django has to ensure cascade deleting functions properly. It has to look for foreign keys to your models Django has to somehow handle pre and post-save signals for your models

If you are sure that your models don't have cascade deleting or any signals to be handled, you can try to use private _raw_delete as follows:

queryset._raw_delete(queryset.db)

You can find more details on it here

8 Python packages that will simplify your life with Django

$
0
0

Django developers, we're devoting this month's python column to packages that will help you. These are our favorite Django libraries for saving time, cutting down on boilerplate code, and generally simplifying our lives. We've got six packages for Django apps and two for Django's REST Framework, and we're not kidding when we say these packages show up in almost every project we work on.

But first, see our tips for making the Django Admin more secure and an article on 5 favorite open source Django packages .

A kitchen sink of useful time-savers: django-extensions

Django-extensions is a favorite Django package chock full of helpful tools like these management commands:

shell_plus starts the Django shell with all your database models already loaded. No more importing from several different apps to test one complex relationship! clean_pyc removes all .pyc projects from everywhere inside your project directory. create_template_tags creates a template tag directory structure inside the app you specify. describe_form displays a form definition for a model, which you can then copy/paste into forms.py. (Note that this produces a regular Django form, not a ModelForm.) notes displays all comments with stuff like TODO, FIXME, etc. throughout your project.

More Python Resources

What is Python? Top Python IDEs Top Python GUI frameworks Latest Python content More developer resources

Django-extensions also includes useful abstract base classes to use for common patterns in your own models. Inherit from these base classes when you create your models to get their:

TimeStampedModel : This base class includes the fields created and modified and a save() method that automatically updates these fields appropriately. ActivatorModel : If your model will need fields like status , activate_date , and deactivate_date , use this base class. It comes with a manager that enables .active() and .inactive() querysets. TitleDescriptionModel and TitleSlugDescriptionModel : These include the title and description fields, and the latter also includes a slug field. The slug field will automatically populate based on the title field.

Django-extensions has more features you may find useful in your projects, so take a tour through its docs !

12-factor-app settings: django-environ

Django-environ allows you to use 12-factor app methodology to manage your settings in your Django project. It collects other libraries, including envparse and honcho . Once you install django-environ, create an .env file at your project's root. Define in that module any settings variables that may change between environments or should remain secret (like API keys, debug status, and database URLs).

Then, in your project's settings.py file, import environ and set up variables for environ.PATH() and environ.Env() according to the example . Access settings variables defined in your .env file with env('VARIABLE_NAME') .

Creating great management commands: django-click

Django-click , based on Click (which we have recommendedbefore…twice), helps you write Django management commands. This library doesn't have extensive documentation, but it does have a directory of test commands in its repository that are pretty useful. A basic Hello World command would look like this:

# app_name.management.commands.hello.py

import djclick as click

@ click. command ( )

@ click. argument ( 'name' )

def command ( name ) :

click. secho ( f 'Hello, {name}'

)

Then in the command line, run:

>> . / manage.py hello Lacey

Hello, Lacey

Handling finite state machines: django-fsm

Django-fsm adds support for finite state machines to your Django models. If you run a news website and need articles to process through states like Writing, Editing, and Published, django-fsm can help you define those states and manage the rules and restrictions around moving from one state to another.

Django-fsm provides an FSMField to use for the model attribute that defines the model instance's state. Then you can use django-fsm's @transition decorator to define methods that move the model instance from one state to another and handle any side effects from that transition.

Although django-fsm is light on documentation, Workflows (States) in Django is a gist that serves as an excellent introduction to both finite state machines and django-fsm.

Contact forms: #django-contact-form

A contact form is such a standard thing on a website. But don't write all that boilerplate code yourself―set yours up in minutes with django-contact-form . It comes with an optional spam-filtering contact form class (and a regular, non-filtering class) and a ContactFormView base class with methods you can override or customize, and it walks you through the templates you will need to create to make your form work.

Registering and authenticating users: django-allauth

Django-allauth is an app that provides views, forms, and URLs for registering users, logging them in and out, resetting their passwords, and authenticating users with outside sites like GitHub or Twitter. It supports email-as-username authentication and is extensively documented. It can be a little confusing to set up the first time you use it; follow the installation instructions carefully and read closely when you customize your settings to make sure you're using all the settings you need to enable a specific feature.

Handling user authentication with Django REST Framework: django-rest-auth

If your Django development includes writing APIs, you're probably using Django REST Framework (DRF). If you're using DRF, you should check out django-rest-auth , a package that enables endpoints for user registration, login/logout, password reset, and social media authentication (by adding django-allauth, which works well with django-rest-auth).

Visualizing a Django REST Framework API: django-rest-swagger

Django REST Swagger provides a feature-rich user interface for interacting with your Django REST Framework API. Once you've installed Django REST Swagger and added it to installed apps, add the Swagger view and URL pattern to your urls.py file; the rest is taken care of in the docstrings of your APIs.

swagger-ui.png
8 Python packages that will simplify your life with Django

The UI for your API will include all your endpoints and available methods broken out by app. It will also list available operations for those endpoints and enable you to interact with the API (adding/deleting/fetching records, for example). It uses the docstrings in your API views to generate documentation for each endpoint, creating a set of API documentation for your project that's useful to you, your frontend developers, and your users.

py.CheckIO: Python in science

$
0
0

py.CheckIO: Python in science

When python and science are being mentioned in one sentence, it usually evokes associations with machine learning, neural networks and artificial intelligence. Nevertheless, there are other scientific areas where Python is used very effectively. For example, physics, mathematics, astronomy and others.

In this article we'll talk about the sciences where using Python is a pretty great idea.

Astronomy

It's no secret that technologies that help to observe stars and remote space objects are developing like everything else. Once they were just the small telescopes, which could be raised by one person, but gradually they became multi-ton stationary facilities.

Nevertheless, in comparison with the centuries-old history of the telescope development, only relatively recently the optical telescopes have started to be replaced by the electronic ones. Therefore, the data obtained this way could be easily processed electronically, which raised the question of choosing a programming language for these exact purposes.

One of the Python’s undoubted advantages is its readability (many of its constructions and expressions look like ordinary English), as well as a low entry threshold. Thus, it would be much easier for the scientists involved in the processing of information obtained from telescopes to learn the basics of Python at the level required for work than any other language with more complex syntax and structure.

For example, one can cite part of the program code written for analyzing the data coming from the Kepler telescope:

def get_data(datadir='', mission='kepler', quarters=None):
'''
Obtain the FGS data for Kepler or K2 directly from MAST
mission = kepler,k2
'''
if isinstance(quarters, int):
quarters = [quarters]
#Find the data for the right mission
if mission == 'kepler':
prefix = 'kplr'
pref = 'q'
if mission == 'k2':
prefix = 'ktwo'
pref = 'c'
if mission != 'kepler' and mission != 'k2':
print("Choose 'kepler' or 'k2' for mission.")
return

As you can see, this part is quite simple and most likely written by the scientist himself for the necessary research. If this code were to be written in C/C++ or Fortran, it would not be so easy to read, and probably the scientist would have to contact the developer regularly to make changes, instead of doing it himself.

By the way, the telescope Kepler, about which we spoke above, looks like this (yes, it’s not on Earth, it’s floating in outer space):


py.CheckIO: Python in science

Also on GitHub is an open repository of the project and you can participate in the improvement of the code, thereby partially partaking in the exploration of outer space).

If you want to learn about other astronomical and astrophysical studies that use Python, you can get acquainted with the performance of Jake Vanderplas.

In addition, for astronomers, there are a number of training lectures that help them to implement Python in their studies and the annual Python in Astronomy conference held in New York.

Mathematics

The queen of sciences also has come a long way through the world history and now she operates with such complex abstractions that some of them a human brain can’t even imagine (like 10-dimensional space).

Before we move on to Python, we'll analyze what problems the researchers might encounter by choosing a different language, for example Java, and consider the situation when they need to work with multidimensional arrays. They aren’t part of the language or the standard library, but can be implemented on top of them with reasonable effort. In fact, there are several implementations in Java, from which you can choose. Therein lies a problem. Suppose you want to use a library for fast Fourier transform (FFT), based on the implementation of arrays "A", together with a linear algebra library based on the implementation of arrays "B". Tough luck - the "A" and "B" arrays are of different types, so you can’t use the output of the FFT as an input to a system of solving linear equations. It doesn’t matter that they are based on the same abstractions, and even that the implementations have much in common. For the Java compiler the types don’t match, end of story.


py.CheckIO: Python in science

Python isn’t completely free of this problem. It’s quite possible to write code in Python or code in a module in C, which expects an exact data type as an input, and otherwise throws an exception. But for Python code this will be considered a bad style, and in C modules for Python too, except for those that require performance or compatibility with other C code. Where possible, Python programmers are expected to use standard interfaces for the work with data. For example, the iteration and indexing work the same for arrays and built-in lists. For operations not supported by the standard interfaces, Python programmers are expected to use Python methods, also subject to "duck typing". In practice, the independently implemented Python types are much more interoperable than the independently implemented Java types. In the specific case of n-dimensional arrays, Python had the chance to accept the overwhelming majority of a single implementation, which is related to questions rather social and historical than technical ones.

Finally, even though Python is a pretty good choice for system integration in scientific computing, it also has limitations: combining Python code with code in other languages with managed memory, say R or Julia, requires a lot of work, and even after that, it remains fragile, because it requires tricks based on undocumented details of implementation. I suspect that the only solution may be the appearance of language-neutral data objects with the possibility of unmanaged access at the byte level, as in C.

In any case, the NumPy library provides excellent opportunities for working with multidimensional arrays and, as mentioned earlier, mastering it for the professional mathematician, who either has no programming experience, or it’s minimal, will be much easier than similar tools of other languages.

If the analysis also assumes visualization (for example, drawing function graphs with the indication of local minimums/maximums or extremes), Python can offer the Matplotlib library, which is definitely up to the task.

Biology

This is a broad enough field, which includes both medical research and projects in the areas of genetic engineering and other cutting-edge industries. As you can imagine, many experiments cannot be carried out in practice for different reasons (financial, ethical or purely technical). In this case, such tools as TensorFlow and SciPy can come to the rescue.

They can help researchers analyze the accident claims figures of diseases in a certain region in order to find the outbreak of the emerging epidemic and prevent it from happening, thus avoiding serious consequences.

Also, high-quality and well-designed simulations

用scrapy镜像网站

$
0
0

近期接了个需求,有一个域名要下线(释放出服务器资源),但是又不希望损失SEO。

本文适合已经开始接触scrapy的朋友,可能会帮助到你。

问题分析

大概想了2个思路:

从内部入手,直接用view层渲染出所有页面。 缺点:需要梳理所有的页面,枚举出所有的URL,这个工作量不小。 从外部入手,直接抓网站的所有页面和依赖资源下来,静态化到本地。

我选择了后者,虽然要做爬虫,但是这样最容易将URL覆盖完整,工作量也仅仅是开发爬虫自身而已。

技术选型

其实镜像网站这个事情,有很多现成的工具,比如wget就支持递归镜像一个网站到本地文件。

还有一些开源项目,就不一一列举了。

我个人试了一下这些工具,最终还是决定自己来研发,它们真的不是很好用,太不可控了。

框架选择python scrapy,这个框架堪称经典,以前也了解过它的架构,而且python语言我还算熟悉,所以就直接入手它了。

制作流程

scrapy上手还是蛮简单的,大家参照 中文版的scrapy文档 自学即可,我一共花了2天时间就基本搞定了爬虫,相信大家也差不多。

下面我仅记录一些特殊问题,解决大家可能碰到的疑惑。

安装依赖

除了安装scrapy框架,我还安装了beautifulsoup4,后者用于解析与修改HTML。

为什么要修改HTML?因为网站依赖了其他域下的js,css文件,需要静态化到本地磁盘,这样就需要修改HTML中的链接地址。

beautifulsoup4可以基于lxml库加速执行,lxml是一个高效解析HTML的C扩展,我们得确保它也被安装。

MIDDLEWARES

scrapy默认会引入N多个中间件,它们被定义为:

SPIDER_MIDDLEWARES_BASE爬虫中间件: 点击查看列表 DOWNLOADER_MIDDLEWARES_BASE下载器中间件: 点击查看列表

这些默认的中间件基本是核心必要功能,不需要我们去关闭。

如果你要关闭,就在settings.py中修改SPIDER_MIDDLEWARES和DOWNLOADER_MIDDLEWARES,将对应的中间件设置为None即可关闭。

parse函数

我们发起的request会指定回调parse函数,我们可以在parse函数中提取页面信息为item,也可以产生新的request请求。

parse函数应该是一个迭代器,它应该用yield返回下面任意一种东西,scrapy会持续迭代直到没有更多东西被yield:

yield要发起的后续request yield要投递给pipeline的item

有一个比较好用的功能,就是request可以配置meta保存一些上下文信息,当parse回调时可以从response中取出meta信息恢复上下文,这样我们就很容易知道当时为何要发起这个request。

链接去重

scrapy默认会在内存中去重抓过的链接,链接会被签名为整形保存。

我在使用scrapy的过程中发现,有的URL始终没有被抓取下来,而且scapy日志也没有对应的报错信息,URL像是凭空消失了一样。

网上碰到类似问题的人不在少数,没有搜到正确答案。

我直接跟了scrapy源码,最后定位了原因:因为scrapy默认将待抓URL存储在一个LIFO队列中,意思就是last in first out,所以当有源源不断的后链进入LIFO时,较老的URL始终无法弹出队列,无法得到及时抓取。(if you findscrapy lost urls and there’s no error logs, you need to change the crawling queue type from LIFO to FIFO in settings.py, this may solve your problem)

解决这个问题只需要在settings.py中配置一下队列类型为FIFO(first in first out)即可:

SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue' 页面修改

scrapy自带了一些HTML的选择器,可以很方便的找到想要的DOM节点。

但是scrapy不支持修改DOM,所以我用了beautifulsoup4这个经典的类库。

beautifulsoup4也支持css选择器,并且可以直接对DOM做修改,使用起来很方便。

思路方面

因为我要镜像某个域名的所有页面,所以我在follow后链的时候,除了下载js和css允许离开本域,其他的后链则要求必须属于本域,否则不会继续follow下载。

当我从HTML提取外链其他域名的js与css的时候,会进行URL链接改写。比如HTML中有一个外链<script src=”http://www.b.com/xx/yy.js”>,那么我下载后会保存在磁盘的$webroot/www.b.com/xx/yy.js中,并且将HTML中的链接改写为:<script src=”/www.b.com/xx/yy.js”>。

另外,对于类似/xx/yy这样没有文件后缀的URL,我会把内容保存在磁盘的/xx/yy/index.html中。

本域下的图片我也会下载,其他域(包括外链与CDN上的图片)的图片我会保留原始链接。

关于HTML改写的思路大概就是这些。

持久化

scrapy支持持久化抓取状态,我没有用到,大家可以根据官方文档试验一下。

文档: https://doc.scrapy.org/en/latest/topics/jobs.html

代码demo

在github我放了一份代码,大家可以简单参考: https://github.com/owenliang/scrapy

Rhea: Efficient environment variables management and typing for python

$
0
0
rhea

Efficient environment variables management and typing for python.

Installation pip install -U rhea Features Typed retrieval of environment variables. Handling of optional, secret, and local variables. Reading from different sources: os, json files, yaml files. Collection of parsed parameters. Usage Reading typed values from a params from rhea import Rhea rhea_config = Rhea(bool_value1='1', bool_value2='false', bool_value3=True) rhea_config.get_boolean('bool_value1') # True rhea_config.get_boolean('bool_value2') # False rhea_config.get_boolean('bool_value3') # True Reading typed values from a env vars import os from rhea import Rhea rhea_config = Rhea.read_configs(os.environ) Reading typed values from different sources import os from rhea import Rhea rhea_config = Rhea.read_configs([os.environ, 'json_file.json', 'yaml_file.yaml', 'another_file_override.json', {'foo': 'bar'}]) Base types

examples:

BOOL_ENV_VALUE1: 1 BOOL_ENV_VALUE2: true BOOL_ENV_VALUE3: f BOOL_ENV_VALUE4: on INT_ENV_VALUE1: '1' INT_ENV_VALUE2: -100 STRING_ENV_VALUE: 'some string' FLOAT_ENV_VALUE1: '1.1' FLOAT_ENV_VALUE2: -1.3 FLOAT_ENV_VALUE3: 1111.1 FLOAT_ENV_VALUE4: -33 DICT_ENV_VALUE: {"foo": "bar", "1": "2"} LIST_ENV_VALUE: 'foo, bar, boo' URI_ENV_VALUE1: user:pass@host.com URI_ENV_VALUE2: user:pass@host:4000

Reading:

from rhea import Rhea rhea_config = Rhea.read_configs([...]) rhea_config.get_boolean('BOOL_ENV_VALUE') # True rhea_config.get_boolean('BOOL_ENV_VALUE') # True rhea_config.get_boolean('BOOL_ENV_VALUE') # False rhea_config.get_boolean('BOOL_ENV_VALUE') # True rhea_config.get_int('INT_ENV_VALUE1') # 1 rhea_config.get_int('INT_ENV_VALUE2') # -100 rhea_config.get_string('STRING_ENV_VALUE') # some string rhea_config.get_float('FLOAT_ENV_VALUE1') # 1.1 rhea_config.get_float('FLOAT_ENV_VALUE1') # -1.3 rhea_config.get_float('FLOAT_ENV_VALUE1') # 1111.1 rhea_config.get_float('FLOAT_ENV_VALUE1') # -33.0 rhea_config.get_dict('DICT_ENV_VALUE') # {'foo': 'bar', '1': '2'} rhea_config.get_list('LIST_ENV_VALUE') # ['foo', 'bar', 'boo'] rhea_config.get_uri('URI_ENV_VALUE1') # UriSpec('user', 'pass', 'host') rhea_config.get_uri('URI_ENV_VALUE2') # UriSpec('user', 'pass', 'host:4000') List of base types

examples:

BOOLS_ENV_VALUE: '[1, 0, "true", "false", "t", "f", "on", "off"]' INTS_ENV_VALUE: '[1, 0, -100]' STRINGS_ENV_VALUE: '["some_string", "another_string"]' FLOATS_ENV_VALUE: '[1.1, -1.3, 0.03, 1111.1, 1.]' DICTS_ENV_VALUE: '[{"foo": "bar", "1": 2}, {"foo": "bar", "1": 2}]' DICT_OF_DICTS_ENV_VALUE: '{"key1": {"foo": "bar", "1": 2}, "key2": {"foo": "bar", "1": 2}}' URIS_ENV_VALUE: '["user:pass@host.com", "user:pass@host:4000"]'

Reading:

from rhea import Rhea rhea_config = Rhea.read_configs([...]) rhea_config.get_boolean('BOOLS_ENV_VALUE', is_list=True) # [True, False, True, False, True, False, True, False] rhea_config.get_int('INTS_ENV_VALUE', is_list=True) # [1, 0, -100] rhea_config.get_string('STRINGS_ENV_VALUE', is_list=True) # ['some_string', 'another_string'] rhea_config.get_float('FLOATS_ENV_VALUE', is_list=True) # [1.1, -1.3, 0.03, 1111.1, 1.0] rhea_config.get_dict('DICTS_ENV_VALUE', is_list=True) # [{'foo1': 'bar1', '1': 2}, {'foo2': 'bar2', '3': 4}] rhea_config.get_dict_of_dicts('DICT_OF_DICTS_ENV_VALUE') # {'key1': {'foo': 'bar', '1': 2}, 'key2': {'foo': 'bar', '1': 2}} rhea_config.get_uri('URIS_ENV_VALUE', is_list=True) # [UriSpec('user', 'pass', 'host'), UriSpec('user', 'pass', 'host:4000')] Optional values and default values from rhea import Rhea rhea_config = Rhea.read_configs([...]) rhea_config.get_boolean('BOOL_ENV_VALUE', is_optional=True) # None rhea_config.get_boolean('BOOL_ENV_VALUE', is_optional=True, default=True) # True rhea_config.get_int('INT_ENV_VALUE', is_optional=True) # None rhea_config.get_int('INT_ENV_VALUE', is_optional=True, default=101) # 101 rhea_config.get_float('FLOAT_ENV_VALUE', is_optional=True) # None rhea_config.get_float('FLOAT_ENV_VALUE', is_optional=True, default=-3.3) # -3.3 rhea_config.get_float('STRING_ENV_VALUE', is_optional=True) # None rhea_config.get_float('STRING_ENV_VALUE', is_optional=True, default='default') # default Value validation from rhea import Rhea rhea_config = Rhea.read_configs([...]) # INT_ENV_VALUE = 11 rhea_config.get_int('INT_ENV_VALUE', options=[1, 2, 3]) # raise RheaError rhea_config.get_int('INT_ENV_VALUE', options=[1, 2, 3, 11]) # 11 Parsed params from rhea import Rhea rhea_config = Rhea.read_configs([...]) rhea_config.get_requested_params(include_locals=False, include_secrets=False) # {'key1': 'value1', ...} Example with Django from rhea import Rhea rhea_config = Rhea.read_configs([...]) DEBUG = rhea_config.get_boolean('DJANGO_DEBUG_MODE', is_optional=True, default=False) SECRET_KEY = rhea_config.get_string('POLYAXON_SECRET_KEY', is_secret=True) Running tests

pytest License


Python Mock process for unit testing

$
0
0

Background:

I am currently writing a process monitoring tool (windows and linux) in python and implementing unit test coverage. The process monitor hooks into the Windows API function EnumProcesses on Windows and monitors the /proc directory on Linux to find current processes. The process names and process IDs are then written to a log which is accessible to the unit tests.

Question:

When I unit test the monitoring behavior I need a process to start and terminate. I would love if there would be a (cross-platform?) way to start and terminate a fake system process that I could uniquely name (and track its creation in a unit test).

Initial ideas: I could use subprocess.Popen() to open any system process but this runs into some issues. The unit tests could falsely pass if the process I'm using to test is run by the system as well. Also, the unit tests are run from the command line and any Linux process I can think of suspends the terminal (nano, etc.). I could start a process and track it by its process ID but I'm not exactly sure how to do this without suspending the terminal.

These are just thoughts and observations from initial testing and I would love it if someone could prove me wrong on either of these points.

I am using Python 2.6.6.

Edit:

Get all Linux process IDs:

try: processDirectories = os.listdir(self.PROCESS_DIRECTORY) except IOError: return [] return [pid for pid in processDirectories if pid.isdigit()]

Get all Windows process IDs:

import ctypes, ctypes.wintypes Psapi = ctypes.WinDLL('Psapi.dll') EnumProcesses = self.Psapi.EnumProcesses EnumProcesses.restype = ctypes.wintypes.BOOL count = 50 while True: # Build arguments to EnumProcesses processIds = (ctypes.wintypes.DWORD*count)() size = ctypes.sizeof(processIds) bytes_returned = ctypes.wintypes.DWORD() # Call enum processes to find all processes if self.EnumProcesses(ctypes.byref(processIds), size, ctypes.byref(bytes_returned)): if bytes_returned.value &lt size: return processIds else: # We weren't able to get all the processes so double our size and try again count *= 2 else: print "EnumProcesses failed" sys.exit()

Windows code is from here

edit: this answer is getting long :), but some of my original answer still applies, so I leave it in :)

Your code is not so different from my original answer. Some of my ideas still apply.

When you are writing Unit Test, you want to only test your logic. When you use code that interacts with the operating system, you usually want to mock that part out. The reason being that you don't have much control over the output of those libraries, as you found out. So it's easier to mock those calls.

In this case, there are two libraries that are interacting with the sytem: os.listdir and EnumProcesses . Since you didn't write them, we can easily fake them to return what we need. Which in this case is a list.

But wait, in your comment you mentioned:

"The issue I'm having with it however is that it really doesn't test that my code is seeing new processes on the system but rather that the code is correctly monitoring new items in a list."

The thing is, we don't need to test the code that actually monitors the processes on the system , because it's a third party code. What we need to test is that your code logic handles the returned processes . Because that's the code you wrote. The reason why we are testing over a list, is because that's what your logic is doing. os.listir and EniumProcesses return a list of pids (numeric strings and integers, respectively) and your code acts on that list.

I'm assuming your code is inside a Class (you are using self in your code). I'm also assuming that they are isolated inside their own methods (you are using return ). So this will be sort of what I suggested originally, except with actual code :) Idk if they are in the same class or different classes, but it doesn't really matter.

Linux method

Now, testing your Linux process function is not that difficult. You can patch os.listdir to return a list of pids.

def getLinuxProcess(self): try: processDirectories = os.listdir(self.PROCESS_DIRECTORY) except IOError: return [] return [pid for pid in processDirectories if pid.isdigit()]

Now for the test.

import unittest from fudge import patched_context import os import LinuxProcessClass # class that contains getLinuxProcess method def test_LinuxProcess(self): """Test the logic of our getLinuxProcess. We patch os.listdir and return our own list, because os.listdir returns a list. We do this so that we can control the output (we test *our* logic, not a built-in library's functionality). """ # Test we can parse our pdis fakeProcessIds = ['1', '2', '3'] with patched_context(os, 'listdir', lamba x: fakeProcessIds): myClass = LinuxProcessClass() .... result = myClass.getLinuxProcess() expected = [1, 2, 3] self.assertEqual(result, expected) # Test we can handle IOERROR with patched_context(os, 'listdir', lamba x: raise IOError): myClass = LinuxProcessClass() .... result = myClass.getLinuxProcess() expected = [] self.assertEqual(result, expected) # Test we only get pids fakeProcessIds = ['1', '2', '3', 'do', 'not', 'parse'] ..... Windows method

Testing your Window's method is a little trickier. What I would do is the following:

def prepareWindowsObjects(self): """Create and set up objects needed to get the windows process" ... Psapi = ctypes.WinDLL('Psapi.dll') EnumProcesses = self.Psapi.EnumProcesses EnumProcesses.restype = ctypes.wintypes.BOOL self.EnumProcessses = EnumProcess ... def getWindowsProcess(self): count = 50 while True: .... # Build arguments to EnumProcesses and call enun process if self.EnumProcesses(ctypes.byref(processIds),... .. else: return []

I separated the code into two methods to make it easier to read (I believe you are already doing this). Here is the tricky part, EnumProcesses is using pointers and they are not easy to play with. Another thing is, that I don't know how to work with pointers in Python, so I couldn't tell you of an easy way to mock that out =P

What I can tell you is to simply not test it. Your logic there is very minimal. Besides increasing the size of count , everything else in that function is creating the space EnumProcesses pointers will use. Maybe you can add a limit to the count size but other than that, this method is short and sweet. It returns the windows processes and nothing more. Just what I was asking for in my original comment :)

So leave that method alone. Don't test it. Make sure though, that anything that uses getWindowsProcess and getLinuxProcess get's mocked out as per my original suggestion.

Hopefully this makes more sense :) If it doesn't let me know and maybe we can have a chat session or do a video call or something.

original answer

I'm not exactly sure how to do what you are asking, but whenever I need to test code that depends on some outside force (external libraries, popen or in this case processes) I mock out those parts.

Now, I don't know how your code is structured, but maybe you can do something like this:

def getWindowsProcesses(self, ...): '''Call Windows API function EnumProcesses and return the list of processes ''' # ... call EnumProcesses ... return listOfProcesses def getLinuxProcesses(self, ...): '''Look in /proc dir and return list of processes''' # ... look in /proc ... return listOfProcessses

These two methods only do one thing , get the list of processes. For Windows, it might just be a call to that API and for Linux just reading the /proc dir. That's all, nothing more. The logic for handling the processes will go somewhere else. This makes these methods extremely easy to mock out since their implementations are just API calls that return a list.

Your code can then easy call them:

def getProcesses(...): '''Get the processes running.''' isLinux = # ... logic for determining OS ... if isLinux: processes = getLinuxProcesses(...) else: processes = getWindowsProcesses(...) # ... do something with processes, write to log file, etc ...

In your test, you can then use a mocking library such as Fudge . You mock out these two methods to return what you expect them to return.

This way you'll be testing your logic since you can control what the result will be.

from fudge import patched_context ... def test_getProcesses(self, ...): monitor = MonitorTool(..) # Patch the method that gets the processes. Whenever it gets called, return # our predetermined list. originalProcesses = [....pids...] with patched_context(monitor, "getLinuxProcesses", lamba x: originalProcesses): monitor.getProcesses() # ... assert logic is right ... # Let's "add" some new processes and test that our logic realizes new # processes were added. newProcesses = [...] updatedProcesses = originalProcessses + (newProcesses) with patched_context(monitor, "getLinuxProcesses", lamba x: updatedProcesses): monitor.getProcesses() # ... assert logic caught new processes ... # Let's "kill" our new processes and test that our logic can handle it with patched_context(monitor, "getLinuxProcesses", lamba x: originalProcesses): monitor.getProcesses() # ... assert logic caught processes were 'killed' ...

Keep in mind that if you test your code this way, you won't get 100% code coverage (since your mocked methods won't be run), but this is fine. You're testing your code and not third party's, which is what matters.

Hopefully this might be able to help you. I know it doesn't answer your question, but maybe you can use this to figure out the best way to test your code.

python np.random.multivariate_normal

$
0
0
import numpy as np
mean = [0, 0]
cov = [[1, 0], [0, 5]]
import matplotlib.pyplot as plt
x, y = np.random.multivariate_normal(mean, cov, 100).T
plt.plot(x, y, 'x')
plt.axis('equal')
plt.show()

Parameters:

mean : 1-D array_like, of length N

Mean of the N-dimensional distribution.

cov : 2-D array_like, of shape (N, N)

Covariance matrix of the distribution. It must be symmetric and positive-semidefinite for proper sampling.

size : int or tuple of ints, optional

Given a shape of, for example, (m,n,k), m*n*k samples are generated, and packed in an m-by-n-by-k arrangement. Because each sample is N-dimensional, the output shape is (m,n,k,N). If no shape is specified, a single (N-D) sample is returned.

check_valid : { ‘warn’, ‘raise’, ‘ignore’ }, optional

Behavior when the covariance matrix is not positive semidefinite.

tol : float, optional

Tolerance when checking the singular values in covariance matrix.

Returns:

out : ndarray

The drawn samples, of shape size, if that was provided. If not, the shape is (N,).

In other words, each entry out[i,j,…,:] is an N-dimensional value drawn from the distribution.
python np.random.multivariate_normal
5019

Python pytz

$
0
0

python pytz module allows us to create timezone aware datetime instances.

Table of Contents

2 Python pytz attributes 2.2 all_timezones_set 2.3 common_timezones, common_timezones_set 2.5 country_timezones 3 Python pytz example 4 Converting timezones Python pytz

Python datetime now() function creates the naive datetime instance from the current local system time. However, this function also takes timezone as an argument that should be the implementation of abstract type tzinfo .

Python pytz module provides implementations of tzinfo class that can be used to create timezone aware datetime instances.

Python pytz module can be installed usingPIP command.

Copy

pip install pytz

Python pytz attributes

There are some attributes in pytz module to help us find the supported timezone strings. Let’s look at them.

all_timezones

Returns the list of all the supported timezones by the pytz module.

Copy

import pytz print('all_timezones =', pytz.all_timezones, '\n')

Output:

Copy

all_timezones = ['Africa/Abidjan', 'Africa/Accra', ... , 'UTC', 'Universal', 'W-SU', 'WET', 'Zulu']

The list is very long, the output is just showing some of the values.

all_timezones_set

Returns the set of all the supported timezones.

Copy

print('all_timezones_set =', pytz.all_timezones_set, '\n')

Output:

Copy

all_timezones_set = LazySet({'America/St_Vincent', 'Asia/Thimphu', 'Etc/GMT+9', ... , 'Europe/Guernsey'})

Note that its a set, so the order of elements is not recorded and output in your system may be in different order.

common_timezones, common_timezones_set

Returns the list and set of commonly used timezones.

Copy

print('common_timezones =', pytz.common_timezones, '\n') print('common_timezones_set =', pytz.common_timezones_set, '\n')

Output:

Copy

common_timezones = ['Africa/Abidjan', 'Africa/Accra', ... , 'US/Pacific', 'UTC'] common_timezones_set = LazySet({'America/St_Vincent', 'Asia/Thimphu', ... , 'Europe/Guernsey'}) country_names

Returns a dict of country ISO Alpha-2 Code as key and country full name as value.

Copy

print('country_names =') for key, val in pytz.country_names.items(): print(key, '=', val, end=',') print('\n') print('IN full name =', pytz.country_names['IN'])

Output:

Copy

country_names = AD = Andorra,AE = United Arab Emirates, ... , ZW = Zimbabwe, IN full name = India

country_timezones

Returns a dict of country ISO Alpha-2 Code as key and list of supported timezones as value.

Copy

print('country_timezones =') for key, val in pytz.country_timezones.items(): print(key, '=', val, end=',') print('\n') print('Supported timezones by US =', pytz.country_timezones['US'])

Output:

Copy

country_timezones = AD = ['Europe/Andorra'],AE = ['Asia/Dubai'],...,ZW = ['Africa/Harare'], Supported timezones by US = ['America/New_York', 'America/Detroit', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Indiana/Indianapolis', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Vevay', 'America/Chicago', 'America/Indiana/Tell_City', 'America/Indiana/Knox', 'America/Menominee', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/North_Dakota/Beulah', 'America/Denver', 'America/Boise', 'America/Phoenix', 'America/Los_Angeles', 'America/Anchorage', 'America/Juneau', 'America/Sitka', 'America/Metlakatla', 'America/Yakutat', 'America/Nome', 'America/Adak', 'Pacific/Honolulu'] Python pytz example

Let’s look at some examples of creating datetime instance with timezone information.

Copy

# getting utc timezone utc = pytz.utc # getting timezone by name ist = pytz.timezone('Asia/Kolkata') # getting datetime of specified timezone print('UTC Time =', datetime.now(tz=utc)) print('IST Time =', datetime.now(tz=ist))

Output:

Copy

UTC Time = 2018-09-20 09:16:46.313898+00:00 IST Time = 2018-09-20 14:46:46.313951+05:30

localize()

We can create timezone aware datetime instance from given datetime instance using localize() function. Note that if you are creating current datetime instance then you should use it carefully, otherwise you will get the wrong information if there is a mismatch between local system timezone and pytz timezone provided.

Copy

# using localize() function, my system is on IST timezone local_datetime = ist.localize(datetime.now()) print('IST Current Time =', local_datetime.strftime('%Y-%m-%d %H:%M:%S %Z%z')) print('Wrong UTC Current Time =', utc.localize(datetime.now()).strftime('%Y-%m-%d %H:%M:%S %Z%z'))

Output:

Copy

IST Current Time = 2018-09-20 14:53:54 IST+0530 Wrong UTC Current Time = 2018-09-20 14:53:54 UTC+0000

Notice that I am usingstrftime() function to print timezone information when datetime is formatted to string.

Converting timezones

We can use astimezone() function to get the time into a different timezone. Following code snippet will convert the earlier IST datetime instance to UTC time.

Copy

# converting IST to UTC utc_datetime = local_datetime.astimezone(utc) print('IST Current Time =', local_datetime.strftime('%Y-%m-%d %H:%M:%S %Z%z')) print('UTC Time =', utc_datetime.strftime('%Y-%m-%d %H:%M:%S %Z%z'))

Output:

Copy

IST Current Time = 2018-09-20 14:56:03 IST+0530 UTC Time = 2018-09-20 09:26:03 UTC+0000

You can checkout complete python script and more Python examples from our GitHub Repository .

Reference: PYPI Docs

Python *args and **kwargs

$
0
0

python has a special syntax, * (single asterisk) and ** (double asterisks), that lets you pass a variable number of arguments to a function. By convention, these are written as *args and **kwargs , but only the asterisks are important; you could equally write *vars and **vars to achieve the same result.

*args is used to pass a non-keyworded variable-length argument list to your function. **kwargs lets you pass a keyworded variable-length of arguments to your function.

Python functions

In a traditional Python function, you must explicitly define the number of arguments and parameters.

def func_one(x): print(x) func_one(5) # 5

But what if the number of parameters does not match the number of arguments? For example, if we define one parameter but pass in none we get an error:

func_one() # func_one() missing 1 required positional argument: 'x'

And if we pass in two arguments but only define one parameter we have another error:

func_one(5, 10) # TypeError: func_one() takes 1 positional argument but 2 were given

In Python a function only works if the number of arguments matches the number of parameters.

*args example

By using *args we can attach a variable-length number of arguments into our function.

For example:

def func_var_args(*args): print(args) func_var_args(1, 2, '3') # (1, 2, '3')

This is useful when we don’t know in advance how many arguments we need to pass in.

**kwargs example

If we want to pass a keyworded variable length of arguments to a function, we use **kwargs . It lets us handle named arguments in our function.

As an example:

def func_keyword_arg(**kwargs): print(kwargs) func_keyword_arg(keyword1=10, keyword2='foo') # {'keyword2': 'foo', 'keyword1': 10}

If we wanted to return just the key/value we can use the items() method .

def func_keyword_arg_dict(**kwargs): for key, value in kwargs.items(): print(key,":",value) func_keyword_arg_dict(keyword1=10, keyword2='foo') # keyword2 : foo # keyword1 : 1 *args and **kwargs together

Often *args and **kwargs are used together in a function where we have at least one required argument. In these instances, order matters. Positional-only parameters must come first, so `` args and **kwargs` are placed *after any required arguments.

def one_required_arg(required_arg, *args, **kwargs): print(required_arg) if args: print(args) if kwargs: print(kwargs)

All three of the following will work with this function:

func("required argument") func("required argument", 1, 2, '3') func("required argument", 1, 2, '3', keyword1=4, keyword2="foo") Final thoughts

*args and **kwargs are powerful Python features that can make writing functions a much more enjoyable task. They are also common when extending an existing method in a subclass .

Just remember, normal, positional-only parameters come first. A simple way to remember this is always add *args and **kwargs at the end of your required arguments.

So if we have one required argument, write:

def some_func(required_arg, *args, **kwargs): pass

If there are two required arguments:

def some_func(required_arg1, required_arg2, *args, **kwargs): pass

And so on!

Want to learn how to build web applications and RESTful APIs in Python? Check out my books Django for Beginners and REST APIs with Django .

Django Tips #3: Template Structure

$
0
0

There are two main ways to organize your template structure in Django: the default app-level way and a custom project-level approach.

Option 1: App Level

By default the Django template loader will look within each app for a templates folder. But to avoid namespace issues you also need to repeat the app name in a folder below that before adding your template file.

For example, if we had an example_project with a pages app and a home.html template file, the proper structure would be like this: within the pages app we create a templates directory, then a pages directory, and finally our home.html file.

├── example_project │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py | └── pages | ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ └── views.py | ├── templates | ├── pages | ├── home.html └── manage.py

This is demonstrated in the official Django polls tutorial and works just fine.

Option 2: Project Level

As a Django projects grow in size it’s often more convenient to have all the templates in one place rather than hunting for them within multiple apps. With a single line change to our settings.py file, we can do this.

Update the 'DIRS' config under TEMPLATES as follows, which specifies that in addition to looking for an app-level templates directory, the Django template loader should also look for a project-level templates directory.

# settings.py TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], ... }, ]

Then create a templates directory at the same level as the project. Here’s an example of what it would look like with the home.html file.

├── example_project │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py | └── pages | ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ └── views.py ├── templates ├── home.html └── manage.py Next Steps

Remember the Django template loader will look for an app-level templates directory and then if we update the DIRS setting it will also look for a project-level templates directory. There is no “right” way to organize templates within your Django project but many developers, myself included, prefer the project-level approach.

Want to learn more about Django? Check out my bookDjango for Beginners.

Viewing all 9596 articles
Browse latest View live