【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 – 第四周作业

【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 – 第四周作业【中文】【吴恩达课后编程作业】Course4-卷积神经网络-第四周作业-人脸识别与神经风格转换

大家好,又见面了,我是你们的朋友全栈君。

【中文】【吴恩达课后编程作业】Course 4 – 卷积神经网络 – 第四周作业 – 人脸识别与神经风格转换



上一篇:
【课程4 – 第四周测验】※※※※※
【回到目录】※※※※※下一篇:
【待撰写-课程5 – 第一周测验】



资料下载

  • 本文所使用的资料已上传到百度网盘【点击下载(555.65MB)】,提取码:zcjp ,请在开始之前下载好所需资料,底部不提供代码。

【博主使用的python版本:3.6.2】


第一部分 – 人脸识别


给之前的“欢乐家”添加人脸识别系统

这是第4周的编程作业,在这里你将构建一个人脸识别系统。这里的许多想法来自FaceNet。在课堂中,吴恩达老师也讨论了 DeepFace

人脸识别系统通常被分为两大类:

  • 人脸验证:“这是不是本人呢?”,比如说,在某些机场你能够让系统扫描您的面部并验证您是否为本人从而使得您免人工检票通过海关,又或者某些手机能够使用人脸解锁功能。这些都是1:1匹配问题。

  • 人脸识别:“这个人是谁?”,比如说,在视频中的百度员工进入办公室时的脸部识别视频的介绍,无需使用另外的ID卡。这个是1:K的匹配问题。

 FaceNet可以将人脸图像编码为一个128位数字的向量从而进行学习,通过比较两个这样的向量,那么我们就可以确定这两张图片是否是属于同一个人。

在本节中,你将学到:

  • 实现三元组损失函数。

  • 使用一个已经训练好了的模型来将人脸图像映射到一个128位数字的的向量。

  • 使用这些编码来执行人脸验证和人脸识别。

 在此次练习中,我们使用一个训练好了的模型,该模型使用了“通道优先”的约定来代表卷积网络的激活,而不是在视频中和以前的编程作业中使用的“通道最后”的约定。换句话说,数据的维度是 ( m , n C , n H , n W ) (m,n_C,n_H,n_W) (m,nC,nH,nW)而不是 ( m , n H , n W , n C ) (m,n_H,n_W,n_C) (m,nH,nW,nC),这两种约定在开源实现中都有一定的吸引力,但是在深度学习的社区中还没有统一的标准。

我们先来导入需要的包:

from keras.models import Sequential
from keras.layers import Conv2D, ZeroPadding2D, Activation, Input, concatenate
from keras.models import Model
from keras.layers.normalization import BatchNormalization
from keras.layers.pooling import MaxPooling2D, AveragePooling2D
from keras.layers.merge import Concatenate
from keras.layers.core import Lambda, Flatten, Dense
from keras.initializers import glorot_uniform
from keras.engine.topology import Layer
from keras import backend as K

#------------用于绘制模型细节,可选--------------#
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
from keras.utils import plot_model
#------------------------------------------------#

K.set_image_data_format('channels_first')

import time
import cv2
import os
import numpy as np
from numpy import genfromtxt
import pandas as pd
import tensorflow as tf
import fr_utils
from inception_blocks_v2 import *

%matplotlib inline
%load_ext autoreload
%autoreload 2

np.set_printoptions(threshold=np.nan)

0 – 简单的人脸验证

 在人脸验证中,你需要给出两张照片并想知道是否是同一个人,最简单的方法是逐像素地比较这两幅图像,如果图片之间的误差小于选择的阈值,那么则可能是同一个人。
【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业


**图 1**
 当然,如果你真的这么做的话效果一定会很差,因为像素值的变化在很大程度上是由于光照、人脸的朝向、甚至头部的位置的微小变化等等。接下来与使用原始图像不同的是我们可以让系统学习构建一个编码$f(img)$,对该编码的元素进行比较,可以更准确地判断两幅图像是否属于同一个人。

1 – 将人脸图像编码为128位的向量

1.1 – 使用卷积网络来进行编码

 FaceNet模型需要大量的数据和长时间的训练,因为,遵循在应用深度学习设置中常见的实践,我们要加载其他人已经训练过的权值。在网络的架构上我们遵循Szegedy et al.等人的初始模型。这里我们提供了初始模型的实现方法,你可以打开inception_blocks.py文件来查看是如何实现的。

 关键信息如下:

  • 该网络使用了 96 × 96 96 \times 96 96×96的RGB图像作为输入数据,图像数量为 m m m,输入的数据维度为 ( m , n c , n h , n w ) = ( m , 3 , 96 , 96 ) (m,n_c,n_h,n_w) = (m,3,96,96) (m,nc,nh,nw)=(m,3,96,96).

  • 输出为 ( m , 128 ) (m,128) (m,128)的已经编码的 m m m 128 128 128位的向量。

我们可以运行下面的代码来创建一个人脸识别的模型。

#获取模型
FRmodel = faceRecoModel(input_shape=(3,96,96))

#打印模型的总参数数量
print("参数数量:" + str(FRmodel.count_params()))

执行结果:

参数数量:3743280

我们可以绘制出模型细节(可选):

#------------用于绘制模型细节,可选--------------#
%matplotlib inline
plot_model(FRmodel, to_file='FRmodel.png')
SVG(model_to_dot(FRmodel).create(prog='dot', format='svg'))
#------------------------------------------------#

执行结果: 结果请详见**文章最底部**或者资料中的“FRmodel.png”

 通过使用128神经元全连接层作为最后一层,该模型确保输出是大小为128的编码向量,然后使用比较两个人脸图像的编码如下:
【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业


**图 2**:

通过计算两个编码和阈值之间的误差,可以确定这两幅图是否代表同一个人。

 因此,如果满足下面两个条件的话,编码是一个比较好的方法:

  • 同一个人的两个图像的编码非常相似。

  • 两个不同人物的图像的编码非常不同。

 三元组损失函数将上面的形式实现,它会试图将同一个人的两个图像(对于给定的图和正例)的编码“拉近”,同时将两个不同的人的图像(对于给定的图和负例)进一步“分离”。

【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业



**图 3**:

在下一部分中,我们将从左到右调用图片: Anchor (A), Positive (P), Negative (N)

1.3 – 三元组损失函数

 对于给定的图像 x x x,其编码为 f ( x ) f(x) f(x),其中 f f f为神经网络的计算函数。
【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业

我们将使用三元组图像 ( A , P , N ) (A,P,N) APN进行训练:

  • A A A是“Anchor”,是一个人的图像。

  • P P P是“Positive”,是相对于“Anchor”的同一个人的另外一张图像。

  • N N N是“Negative”,是相对于“Anchor”的不同的人的另外一张图像。

 这些三元组来自训练集,我们使用 ( A ( i ) , P ( i ) , N ( i ) ) (A^{(i)},P^{(i)},N^{(i)}) (A(i),P(i),N(i))来表示第 i i i个训练样本。我们要保证图像 A ( i ) A^{(i)} A(i)与图像 P ( i ) P^{(i)} P(i)的差值至少比与图像 N ( i ) N^{(i)} N(i)的差值相差 α \alpha α

∣ ∣ f ( A ( i ) ) − f ( P ( i ) ) ∣ ∣ 2 2 + α < ∣ ∣ f ( A ( i ) ) − f ( N ( i ) ) ∣ ∣ 2 2 (1) \mid \mid f(A^{(i)}) – f(P^{(i)}) \mid \mid_2^2 + \alpha \quad < \quad \mid \mid f(A^{(i)}) – f(N^{(i)}) \mid \mid_2^2 \tag{1} f(A(i))f(P(i))22+α<f(A(i))f(N(i))22(1)

 我们希望让三元组损失变为最小:

J = ∑ i = 1 m [ ∣ ∣ f ( A ( i ) ) − f ( P ( i ) ) ∣ ∣ 2 2 ⏟ (2) − ∣ ∣ f ( A ( i ) ) − f ( N ( i ) ) ∣ ∣ 2 2 ⏟ (3) + α ] + (4) \mathcal{J} = \sum^{m}_{i=1} \large[ \small \underbrace{\mid \mid f(A^{(i)}) – f(P^{(i)}) \mid \mid_2^2}_\text{(2)} – \underbrace{\mid \mid f(A^{(i)}) – f(N^{(i)}) \mid \mid_2^2}_\text{(3)} + \alpha \large ] \small_+ \tag{4} J=i=1m[(2)


f(A(i))f(P(i))22
(3)


f(A(i))f(N(i))22
+
α]+(4)

  • 在这里,我们使用“ [ ⋅ ⋅ ⋅ ] + [···]_+ []+”来表示函数 m a x ( z , 0 ) max(z,0) max(z,0)

需要注意的是:

  • 公式(2)是给定三元组 A A A与正例 P P P之间的距离的平方,我们要让它变小。

  • 公式(3)是给定三元组 A A A与负例 N N N之间的距离的平方,我们要让它变大,经公式(1)变换后前面偶一个负号。

  • α \alpha α是间距,这个需要我们来手动选择,这里我们使用 α = 0.2 \alpha = 0.2 α=0.2

 大多数实现将编码归一化为范数等于1的向量,即( ∣ ∣ f ( i m g ) ∣ ∣ 2 \mid \mid f(img)\mid \mid_2 f(img)2=1),这里你没必要操心这个。现在我们要实现公式(4),由以下4步构成:

  1. 计算”anchor” 与 “positive”之间编码的距离: ∣ ∣ f ( A ( i ) ) − f ( P ( i ) ) ∣ ∣ 2 2 \mid \mid f(A^{(i)}) – f(P^{(i)}) \mid \mid_2^2 f(A(i))f(P(i))22

  2. 计算”anchor” 与 “negative”之间编码的距离: ∣ ∣ f ( A ( i ) ) − f ( N ( i ) ) ∣ ∣ 2 2 \mid \mid f(A^{(i)}) – f(N^{(i)}) \mid \mid_2^2 f(A(i))f(N(i))22

  3. 根据公式计算每个样本的值:$ \mid \mid f(A^{(i)}) – f(P^{(i)}) \mid – \mid \mid f(A^{(i)}) – f(N^{(i)}) \mid \mid_2^2 + \alpha$

  4. 通过取带零的最大值和对训练样本的求和来计算整个公式: J = ∑ i = 1 m [ ∣ ∣ f ( A ( i ) ) − f ( P ( i ) ) ∣ ∣ 2 2 − ∣ ∣ f ( A ( i ) ) − f ( N ( i ) ) ∣ ∣ 2 2 + α ] + (4) \mathcal{J} = \sum^{m}_{i=1} \large[ \small \mid \mid f(A^{(i)}) – f(P^{(i)}) \mid \mid_2^2 – \mid \mid f(A^{(i)}) – f(N^{(i)}) \mid \mid_2^2+ \alpha \large ] \small_+ \tag{4} J=i=1m[f(A(i))f(P(i))22f(A(i))f(N(i))22+α]+(4)

 一些会用到的函数:tf.reduce_sum()tf.square()tf.subtract()tf.add(), tf.maximum(),对于步骤1与步骤2,需要对 ∣ ∣ f ( A ( i ) ) − f ( P ( i ) ) ∣ ∣ 2 2 \mid \mid f(A^{(i)}) – f(P^{(i)}) \mid \mid_2^2 f(A(i))f(P(i))22 ∣ ∣ f ( A ( i ) ) − f ( N ( i ) ) ∣ ∣ 2 2 \mid \mid f(A^{(i)}) – f(N^{(i)}) \mid \mid_2^2 f(A(i))f(N(i))22的其中的项进行求和,对于步骤4,你需要对整个训练集进行求和。

def triplet_loss(y_true, y_pred, alpha = 0.2):
    """ 根据公式(4)实现三元组损失函数 参数: y_true -- true标签,当你在Keras里定义了一个损失函数的时候需要它,但是这里不需要。 y_pred -- 列表类型,包含了如下参数: anchor -- 给定的“anchor”图像的编码,维度为(None,128) positive -- “positive”图像的编码,维度为(None,128) negative -- “negative”图像的编码,维度为(None,128) alpha -- 超参数,阈值 返回: loss -- 实数,损失的值 """
    #获取anchor, positive, negative的图像编码
    anchor, positive, negative = y_pred[0], y_pred[1], y_pred[2]
    
    #第一步:计算"anchor" 与 "positive"之间编码的距离,这里需要使用axis=-1
    pos_dist = tf.reduce_sum(tf.square(tf.subtract(anchor,positive)),axis=-1)
    
    #第二步:计算"anchor" 与 "negative"之间编码的距离,这里需要使用axis=-1
    neg_dist = tf.reduce_sum(tf.square(tf.subtract(anchor,negative)),axis=-1)
    
    #第三步:减去之前的两个距离,然后加上alpha
    basic_loss = tf.add(tf.subtract(pos_dist,neg_dist),alpha)
    
    #通过取带零的最大值和对训练样本的求和来计算整个公式
    loss = tf.reduce_sum(tf.maximum(basic_loss,0))
    
    return loss

我们来测试一下:

with tf.Session() as test:
    tf.set_random_seed(1)
    y_true = (None, None, None)
    y_pred = (tf.random_normal([3, 128], mean=6, stddev=0.1, seed = 1),
              tf.random_normal([3, 128], mean=1, stddev=1, seed = 1),
              tf.random_normal([3, 128], mean=3, stddev=4, seed = 1))
    loss = triplet_loss(y_true, y_pred)
    
    print("loss = " + str(loss.eval()))

测试结果:

loss = 528.143

2 – 加载训练好了的模型

 FaceNet是通过最小化三元组损失来训练的,但是由于训练需要大量的数据和时间,所以我们不会从头训练,相反,我们会加载一个已经训练好了的模型,运行下列代码来加载模型,可能会需要几分钟的时间。

#开始时间
start_time = time.clock()

#编译模型
FRmodel.compile(optimizer = 'adam', loss = triplet_loss, metrics = ['accuracy'])

#加载权值
fr_utils.load_weights_from_FaceNet(FRmodel)

#结束时间
end_time = time.clock()

#计算时差
minium = end_time - start_time

print("执行了:" + str(int(minium / 60)) + "分" + str(int(minium%60)) + "秒")

执行结果:

执行了:1分48秒

这里有一些3个人之间的编码距离的例子:
【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业


**图 4**:

三个人的编码之间的距离的输出示例

现在我们使用这个模型进行人脸验证和人脸识别。

3 – 模型的应用

 之前我们对“欢乐家”添加了笑脸识别,现在我们要构建一个面部验证系统,以便只允许来自指定列表的人员进入。为了通过门禁,每个人都必须在门口刷身份证以表明自己的身份,然后人脸识别系统将检查他们到底是谁。

3.1 – 人脸验证

 我们构建一个数据库,里面包含了允许进入的人员的编码向量,我们使用fr_uitls.img_to_encoding(image_path, model)函数来生成编码,它会根据图像来进行模型的前向传播。
 我们这里的数据库使用的是一个字典来表示,这个字典将每个人的名字映射到他们面部的128维编码上。

database = { 
   }
database["danielle"] = fr_utils.img_to_encoding("images/danielle.png", FRmodel)
database["younes"] = fr_utils.img_to_encoding("images/younes.jpg", FRmodel)
database["tian"] = fr_utils.img_to_encoding("images/tian.jpg", FRmodel)
database["andrew"] = fr_utils.img_to_encoding("images/andrew.jpg", FRmodel)
database["kian"] = fr_utils.img_to_encoding("images/kian.jpg", FRmodel)
database["dan"] = fr_utils.img_to_encoding("images/dan.jpg", FRmodel)
database["sebastiano"] = fr_utils.img_to_encoding("images/sebastiano.jpg", FRmodel)
database["bertrand"] = fr_utils.img_to_encoding("images/bertrand.jpg", FRmodel)
database["kevin"] = fr_utils.img_to_encoding("images/kevin.jpg", FRmodel)
database["felix"] = fr_utils.img_to_encoding("images/felix.jpg", FRmodel)
database["benoit"] = fr_utils.img_to_encoding("images/benoit.jpg", FRmodel)
database["arnaud"] = fr_utils.img_to_encoding("images/arnaud.jpg", FRmodel)

 现在,当有人出现在你的门前刷他们的身份证的时候,你可以在数据库中查找他们的编码,用它来检查站在门前的人是否与身份证上的名字匹配。

 现在我们要实现 verify() 函数来验证摄像头的照片(image_path)是否与身份证上的名称匹配,这个部分可由以下步骤构成:

  1. 根据image_path来计算编码。

  2. 计算与存储在数据库中的身份图像的编码的差距。

  3. 如果差距小于0.7,那么就打开门,否则就不开门。

 如上所述,我们使用L2(np.linalg.norm)来计算差距。(注意:在本实现中,将L2的误差(而不是L2误差的平方)与阈值0.7进行比较。)

def verify(image_path, identity, database, model):
    """ 对“identity”与“image_path”的编码进行验证。 参数: image_path -- 摄像头的图片。 identity -- 字符类型,想要验证的人的名字。 database -- 字典类型,包含了成员的名字信息与对应的编码。 model -- 在Keras的模型的实例。 返回: dist -- 摄像头的图片与数据库中的图片的编码的差距。 is_open_door -- boolean,是否该开门。 """
    #第一步:计算图像的编码,使用fr_utils.img_to_encoding()来计算。
    encoding = fr_utils.img_to_encoding(image_path, model)
    
    #第二步:计算与数据库中保存的编码的差距
    dist = np.linalg.norm(encoding - database[identity])
    
    #第三步:判断是否打开门
    if dist < 0.7:
        print("欢迎 " + str(identity) + "回家!")
        is_door_open = True
    else:
        print("经验证,您与" + str(identity) + "不符!")
        is_door_open = False
    
    return dist, is_door_open

 现在younes在门外,相机已经拍下了照片并存放在了(“images/camera_0.jpg”),现在我们来验证一下~
【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业

verify("images/camera_0.jpg","younes",database,FRmodel)

执行结果:

欢迎 younes回家!
(0.65939206, True)

 Benoit已经被禁止进入,也从数据库中删除了自己的信息,他偷了Kian的身份证并试图通过门禁,我们来看看他能不能进入呢?
【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业

verify("images/camera_2.jpg", "kian", database, FRmodel)

执行结果:

经验证,您与kian不符!
(0.86224037, False)

3.2 – 人脸识别

 面部验证系统基本运行良好,但是自从Kian的身份证被偷后,那天晚上他回到房子那里就不能进去了!为了减少这种恶作剧,你想把你的面部验证系统升级成面部识别系统。这样就不用再带身份证了,一个被授权的人只要走到房子前面,前门就会自动为他们打开!

 我们将实现一个人脸识别系统,该系统将图像作为输入,并确定它是否是授权人员之一(如果是,是谁),与之前的人脸验证系统不同,我们不再将一个人的名字作为输入的一部分。

 现在我们要实现who_is_it()函数,实现它需要有以下步骤:

  1. 根据image_path计算图像的编码。

  2. 从数据库中找出与目标编码具有最小差距的编码。

    • 初始化min_dist变量为足够大的数字(100),它将找到与输入的编码最接近的编码。
    • 遍历数据库中的名字与编码,可以使用for (name, db_enc) in database.items()语句。
      • 计算目标编码与当前数据库编码之间的L2差距。
      • 如果差距小于min_dist,那么就更新名字与编码到identity与min_dist中。
def who_is_it(image_path, database,model):
    """ 根据指定的图片来进行人脸识别 参数: images_path -- 图像地址 database -- 包含了名字与编码的字典 model -- 在Keras中的模型的实例。 返回: min_dist -- 在数据库中与指定图像最相近的编码。 identity -- 字符串类型,与min_dist编码相对应的名字。 """
    #步骤1:计算指定图像的编码,使用fr_utils.img_to_encoding()来计算。
    encoding = fr_utils.img_to_encoding(image_path, model)
    
    #步骤2 :找到最相近的编码
    ## 初始化min_dist变量为足够大的数字,这里设置为100
    min_dist = 100
    
    ## 遍历数据库找到最相近的编码
    for (name,db_enc) in database.items():
        ### 计算目标编码与当前数据库编码之间的L2差距。
        dist = np.linalg.norm(encoding - db_enc)
        
        ### 如果差距小于min_dist,那么就更新名字与编码到identity与min_dist中。
        if dist < min_dist:
            min_dist = dist
            identity = name
    
    # 判断是否在数据库中
    if min_dist > 0.7:
        print("抱歉,您的信息不在数据库中。")
        
    else:
        print("姓名" + str(identity) + " 差距:" + str(min_dist))
    
    return min_dist, identity

 Younes站在前门,相机给他拍了张照片(“images/camera_0.jpg”)。让我们看看who_it_is()算法是否识别Younes。

【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业

who_is_it("images/camera_0.jpg", database, FRmodel)

执行结果:

姓名younes  差距:0.659392
(0.65939206, 'younes')

请记住:

  • 人脸验证解决了更容易的1:1匹配问题,人脸识别解决了更难的1∶k匹配问题。

  • 三重损失是训练神经网络学习人脸图像编码的一种有效的损失函数。

  • 相同的编码可用于验证和识别。测量两个图像编码之间的距离可以确定它们是否是同一个人的图片。


第二部分 – 神经风格转换


深度学习在艺术上的应用:神经风格转换

 这是本周的第二个编程作业,在这里我们将学习到如何进行神经风格转换,这个算法是Gatys创立的(2015,https://arxiv.org/abs/1508.06576 )。

在这里,我们将:

  • 实现神经风格转换算法

  • 用算法生成新的艺术图像

 在之前的学习中我们都是优化了一个成本函数来获得一组参数值,在这里我们将优化成本函数以获取像素值,我们先来导入包:

import time
import os
import sys
import scipy.io
import scipy.misc
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
from PIL import Image
import nst_utils
import numpy as np
import tensorflow as tf

%matplotlib inline

1 – 问题描述

 神经风格转换(Neural Style Transfer,NST)是深学习中最有趣的技术之一。如下图所示,它合并两个图像,即“内容”图像( C C Content)和“风格”图像( S S Style),以创建“生成的”图像( G G Generated)。生成的图像G将图像C的“内容”与图像S的“风格”相结合。

 在这个例子中,你将生成一个巴黎卢浮宫博物馆(内容图像C)与一个领袖印象派运动克劳德·莫奈的画(风格图像S)混合起来的绘画。

【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业

我们来看看到底该怎样做~

2 – 迁移学习

 神经风格转换(NST)使用先前训练好了的卷积网络,并在此基础之上进行构建。使用在不同任务上训练的网络并将其应用于新任务的想法称为迁移学习。

 根据原始的NST论文(https://arxiv.org/abs/1508.06576 ),我们将使用VGG网络,具体地说,我们将使用VGG-19,这是VGG网络的19层版本。这个模型已经在非常大的ImageNet数据库上进行了训练,因此学会了识别各种低级特征(浅层)和高级特征(深层)。

 运行以下代码从VGG模型加载参数。这可能需要几秒钟的时间。

model = nst_utils.load_vgg_model("pretrained-model/imagenet-vgg-verydeep-19.mat")

print(model)

执行结果:

{'input': <tf.Variable 'Variable:0' shape=(1, 300, 400, 3) dtype=float32_ref>, 'conv1_1': <tf.Tensor 'Relu:0' shape=(1, 300, 400, 64) dtype=float32>, 'conv1_2': <tf.Tensor 'Relu_1:0' shape=(1, 300, 400, 64) dtype=float32>, 'avgpool1': <tf.Tensor 'AvgPool:0' shape=(1, 150, 200, 64) dtype=float32>, 'conv2_1': <tf.Tensor 'Relu_2:0' shape=(1, 150, 200, 128) dtype=float32>, 'conv2_2': <tf.Tensor 'Relu_3:0' shape=(1, 150, 200, 128) dtype=float32>, 'avgpool2': <tf.Tensor 'AvgPool_1:0' shape=(1, 75, 100, 128) dtype=float32>, 'conv3_1': <tf.Tensor 'Relu_4:0' shape=(1, 75, 100, 256) dtype=float32>, 'conv3_2': <tf.Tensor 'Relu_5:0' shape=(1, 75, 100, 256) dtype=float32>, 'conv3_3': <tf.Tensor 'Relu_6:0' shape=(1, 75, 100, 256) dtype=float32>, 'conv3_4': <tf.Tensor 'Relu_7:0' shape=(1, 75, 100, 256) dtype=float32>, 'avgpool3': <tf.Tensor 'AvgPool_2:0' shape=(1, 38, 50, 256) dtype=float32>, 'conv4_1': <tf.Tensor 'Relu_8:0' shape=(1, 38, 50, 512) dtype=float32>, 'conv4_2': <tf.Tensor 'Relu_9:0' shape=(1, 38, 50, 512) dtype=float32>, 'conv4_3': <tf.Tensor 'Relu_10:0' shape=(1, 38, 50, 512) dtype=float32>, 'conv4_4': <tf.Tensor 'Relu_11:0' shape=(1, 38, 50, 512) dtype=float32>, 'avgpool4': <tf.Tensor 'AvgPool_3:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_1': <tf.Tensor 'Relu_12:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_2': <tf.Tensor 'Relu_13:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_3': <tf.Tensor 'Relu_14:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_4': <tf.Tensor 'Relu_15:0' shape=(1, 19, 25, 512) dtype=float32>, 'avgpool5': <tf.Tensor 'AvgPool_4:0' shape=(1, 10, 13, 512) dtype=float32>}

 该模型存储在一个python字典中,其中每个变量名都是键,相应的值是一个包含该变量值的张量,要通过此网络运行图像,只需将图像提供给模型。 在TensorFlow中,你可以使用tf.assign函数来做到这一点:

#tf.assign函数用法
model["input"].assign(image)

 这将图像作为输入给模型,在此之后,如果想要访问某个特定层的激活,比如4_2,请这样做:

#访问 4_2 层的激活
sess.run(model["conv4_2"])

3 – 神经风格转换

 我们可以使用下面3个步骤来构建神经风格转换(Neural Style Transfer,NST)算法:

  • 构建内容损失函数 J c o n t e n t ( C , G ) J_{content}(C,G) Jcontent(C,G)

  • 构建风格损失函数 J s t y l e ( S , G ) J_{style}(S,G) Jstyle(S,G)

  • 把它放在一起得到 J ( G ) = α J c o n t e n t ( C , G ) + β J s t y l e ( S , G ) J(G) = \alpha J_{content}(C,G) + \beta J_{style}(S,G) J(G)=αJcontent(C,G)+βJstyle(S,G).

3.1 – 计算内容损失

 在我们的运行的例子中,内容图像C是巴黎卢浮宫博物馆的图片,运行下面的代码来看看卢浮宫的图片:

content_image = scipy.misc.imread("images/louvre.jpg")
imshow(content_image)

【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业

内容图片©显示了卢浮宫的金字塔被旧的巴黎建筑包围,图片上还有阳光灿烂的天空和一些云彩。

3.1.1 – 如何确保生成的图像G与图像C的内容匹配?

 正如我们在视频中看到的,浅层的一个卷积网络往往检测到较低层次的特征,如边缘和简单的纹理,更深层往往检测更高层次的特征,如更复杂的纹理以及对象分类等。

 我们希望“生成的”图像G具有与输入图像C相似的内容。假设我们选择了一些层的激活来表示图像的内容,在实践中,如果你在网络中间选择一个层——既不太浅也不太深,你会得到最好的的视觉结果。(当你完成了这个练习后,你可以用不同的图层进行实验,看看结果是如何变化的。)

 假设你选择了一个特殊的隐藏层,现在,将图像C作为已经训练好的VGG网络的输入,然后进行前向传播。让 a ( C ) a^{(C)} a(C)成为你选择的层中的隐藏层激活(在视频中吴恩达老师写作 a [ l ] ( C ) a^{[l](C)} a[l](C),但在这里我们将去掉上标 [ l ] [l] [l]以简化符号),激活值为 n H × n W × n C n_H \times n_W \times n_C nH×nW×nC的张量。然后用图像G重复这个过程:将G设置为输入数据,并进行前向传播,让 a ( G ) a^{(G)} a(G)成为相应的隐层激活,我们将把内容成本函数定义为:

J c o n t e n t ( C , G ) = 1 4 ×   n H × n W × n C ∑ 所 有 条 目 ( a ( C ) − a ( G ) ) 2 (1) J_{content}(C,G) = \frac{1}{4 \times \ n_H \times n_W \times n_C }\sum_{所有条目}(a^{(C)} – a^{(G)})^2 \tag{1} Jcontent(C,G)=4× nH×nW×nC1(a(C)a(G))2(1)

博主注: 1 4 \frac{1}{4} 41的出处请详见视频4.9内容代价函数视频,于2:24处,吴恩达老师提及到“也可以在前面加上归一化或者不加,比如 1 2 \frac{1}{2} 21或者其他的,都影响不大”。

 这里 n H , n W , n C n_H, n_W, n_C nH,nW,nC分别代表了你选择的隐藏层的高度、宽度、通道数,并出现在成本的归一化项中,为了使得方便理解,需要注意的是 a ( C ) a^{(C)} a(C) 与 $ a^{(G)}$ 是与隐藏层激活相对应的卷积值,为了计算成本 J c o n t e n t ( C , G ) J_{content}(C,G) Jcontent(C,G),可以方便地将这些3D卷积展开为2D矩阵,如下所示。(从技术上讲,不需要这个展开步骤来计算 J c o n t e n t J_{content} Jcontent,但是当您以后需要执行类似的操作来计算 J s t y l e J_{style} Jstyle时,这将是一个很好的实践。)

【中英】【吴恩达课后编程作业】Course 4 -卷积神经网络 - 第四周作业

现在我们要使用tensorflow来实现内容代价函数,它由以下3步构成:

  1. 从a_G中获取维度信息:

    • 从张量X中获取维度信息,可以使用:X.get_shape().as_list()
  2. 将a_C与a_G如上图一样降维:

  3. 计算内容代价:

def compute_content_cost(a_C, a_G):
    """ 计算内容代价的函数 参数: a_C -- tensor类型,维度为(1, n_H, n_W, n_C),表示隐藏层中图像C的内容的激活值。 a_G -- tensor类型,维度为(1, n_H, n_W, n_C),表示隐藏层中图像G的内容的激活值。 返回: J_content -- 实数,用上面的公式1计算的值。 """
    
    #获取a_G的维度信息
    m, n_H, n_W, n_C = a_G.get_shape().as_list()
    
    #对a_C与a_G从3维降到2维
    a_C_unrolled = tf.transpose(tf.reshape(a_C, [n_H * n_W, n_C]))
    a_G_unrolled = tf.transpose(tf.reshape(a_G, [n_H * n_W, n_C]))
    
    #计算内容代价
    #J_content = (1 / (4 * n_H * n_W * n_C)) * tf.reduce_sum(tf.square(tf.subtract(a_C_unrolled, a_G_unrolled)))
    J_content = 1/(4*n_H*n_W*n_C)*tf.reduce_sum(tf.square(tf.subtract(a_C_unrolled, a_G_unrolled)))
    return J_content

我们来测试一下:

tf.reset_default_graph()

with tf.Session() as test:
    tf.set_random_seed(1)
    a_C = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4)
    a_G = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4)
    J_content = compute_content_cost(a_C, a_G)
    print("J_content = " + str(J_content.eval()))
    
    test.close()

测试结果:

J_content = 6.76559

需要记住的是:

  • 内容成本采用神经网络的隐层激活,并测量 a ( C ) a^{(C)} a(C) a ( G ) a^{(G)} a(G)的区别。

  • 当我们以后最小化内容成本时,这将有助于确保 G G G的内容与 C C C相似。

3.2 – 计算风格损失

我们先来看一下下面的风格图片:

style_image = scipy.misc.imread("images/monet_800600.jpg")

imshow(style_image)

执行结果:
<img src=”data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVMAAAD8CAYAAADKdkf7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzsvcvOZVuW3/UbY97W2nt/l4g4l8yqTNtlu0pCogMyINFEQkLu0AUewC0egCdAokebhts8gSWewRINBFjG5XIVztvJzDjxXfbea83roDF3xDlcZEFlpjgqxZDiRHzfvqy15mXMMf7j/x9HzIzP9tk+22f7bL+b6f/fN/DZPttn+2x/E+yzM/1sn+2zfbbfg312pp/ts322z/Z7sM/O9LN9ts/22X4P9tmZfrbP9tk+2+/BPjvTz/bZPttn+z3YH8SZish/IiL/XET+XET+qz/ENT7bZ/tsn+2HZPL75pmKiAP+N+A/Bn4G/FPgPzez//X3eqHP9tk+22f7AdkfIjL994E/N7O/MLMC/PfAf/oHuM5n+2yf7bP9YMz/Ab7zj4F//b2ffwb8B/+mD9zdP9gXX3yJiDDMEASR6ekNmW8SMJs/mYEI85Xbf26vYAamjloHwwwTQTGcgoiholxzA+cZn4JyxQTEQEUQEfj4mkEIHjPoYyCiqMx76DbmrQmIDUBIzgg68E7wTmEMRAxR/T99J2Y4p/NZnMxnEwEbIPrpWURuF7g9qt3+9en9t9/M98r3LvBx0MA+jQ4IMp9V5nf0MZ9hGPThaDbHOXqHzRtFP15fhG5GLoM+5u+8Ct6B00F0ilmf1xrjNi9KbvPffRjdHK1DN8FsgICKY1ifDwF4JzgFTBB19D7HMDhIQQh0einobWzUe/g4b8h8Xvlu3WB2e/bv2ceM7DbW343ZbYz43q8FYGA4hgkDaMMYNt/pnGOMhpnM9SbgACdQxxx9RfDa0Y+zIN+bJ/n+/PPpojYn73b/H18XBgNVxcaYe6bPnz/um5ltzvH9OM+fnk1v44MgwLAxl9yntTMX96B/NzS3W1IVRv/uuz4NoQjjto5U5bvXbusMAxHFsDnn310J+3S/3N733XN/NzMfV+58rzFfGx8n6Hs3KiJz7X0/4Zbv/3V7bgy97cmP1/uf/vmf/9bMvuSvaX8IZyr/D7/7v2EJIvKPgH8E8MUXX/Jf/zf/LdEnRt3w6nBOaMPAlLisjNEYo1FLwS8reXtlXRytDUJ6S7YE1hkmbFtE18jeN1ou1HYFN7A6KKb8xTYoJXJ8eEt0kWt5YYjiUQ7+8bY4IAgE59m2jbCsbLVzWhaCOHItbL3hRGF0giayGV9IZvGFd3fwd952/v7bwHFRrA/KthNV8c7hAvgUQY3e50ZR1TnBOqdl0HHOfVpwJm5uOqeUvZJ8mL+3jopRa8X7yFZ3RByIQ2/fa2YEnf/uXql7pfRO753jciQ3YcQD2EYrlVPotDEY1ljSgbxdGCi7Nczfc742vnqbcMVITrH2Cr2TSyeYsF+upPtHNivkbUc08WIP/KtfBa51w6Uv2HLlfO1cmuFdBiuoBJLvPNyvtAYxrNRaOaXGjx4rX3ljrc+UesENR3CeGA/4xwPhsNBKZVSI0bONgr+NqXhBhtFrQxRGyfTecWvCq6Pmue6EgeHxIdFaY7tsuN5o6vjVJfG/txWNiXMW3ID7w5F9v7KdO+fXQjysfPVY+foBHlzmZYeqC5dL5yc/chzaM84FlOnooneMVhER6ujIMEaveHPYmIf7MKP1DENQc9NTYxTLc257xxEppZICt+93QKO1Qe+d5ZAYY2AqtNZIKdFywfoAlLJnnHMEDdRaadZwEegO00a7FHQYmhy9VFQV5wIDUFVyzogZIQT2fWc5HTHrc925BfWO1hqlF3reiNFTe0H9QmmVxQcAcq244Bm9Em5+t9ERmZ8/LEe2yytiUCzjo6ObYwjYaCiO3iuuDWw0XPD03gHwKAND1NNaw6eIquK9hz746j/8h3/1uzi+P0Sa/zPgp9/7+SfAL/6vbzKz/87M/oGZ/YPT3R2YEb0SnLKmcIv0oPc+J4qBU8U5wUknxkC/nbxjdLBO742aC0EHzgrJCsc0uF89QaAatBkjcEgH1hBRhIO/5+hPHPSExxilckyRU1wIGnDOca0ZESFvO9Y7wXnenu5JzhPEoTJIydCUiOs7ni7K8fgl1+appgwGaYkMazgnLHdH1Ds0OFSVEMLc9G5e71OkooIN+eRIVZXRwctcwGY2N48qzjl678S4zO8SwUeHC4oqiBpGx3pFAIcQY2S/Zuq24doLsewso5L3QXQrzhYu50be4XIu3KnnnYe/8xCQ/ZX6+sz2/MponXzdYTjqyytBhbZfkAandOKyJ85l8PZB+dO/dced/ZYv04UfP8BDbJwCHFJk1IYPK+dL5ng4kXMmOCF4h0pDj0J8fMC/e6QqvFw3ttLp5qh1OqUZKQ6cc2BziY9+i4BU6L3Tx8DMpoMxm4di6/RSgUEdda5TMTQ2tl65DOFwdKRFOa7KUMf7y8br3rl2ofsAMbDVyIfXQWNlTRHphVJeOZ+vDASTgTlly5XrnjGZ9xidxzmH9/7Tc9RaMQaKoAbBKS1novOfNq8TRQ28d2jQW5Q4yPuOGPgY6L3SWoMOMuZhN1pHTBmt40QIt/UjDNQUusOLx5qi6klxpWwF7yPeR2rveO/JdR5a3ntqraj3lLIz+sxSZBg1F2reOa0H7o8nap3rdLRGcB6GMUZjiZ7FO6LzeHWEEPDOoQpxjez7FetzPJxzWB/IMJIKAY+1jjP9FIQwbI6BOtropDWRYiR4Pw+uMWA0atl/Z8f3h4hM/ynwpyLyJ8DPgf8M+C/+TR+YqUhn33ecOEprdBsoiksepx6sE1KktMy+76R1BTshwcALtmWcGhoF5zImHh2DXTP5WjCNfLOd8Sy8Wd+AJCg7fRiH9IDZQMUIXkADa3AwPKUPHA43GiEoizr8LZ2T2litsxwizgZ5ZN6uD4w2WI9v+Jd/+S3707/iH/5H/w55P3O3HgjLHVindYf3ia28zs2vgqrD+pzgGaEaDGH0hpiCCvQxUyUbBD+3k5khfgFrqDeck3na8jG6nZt0VHAfU0o/8GFGrBw9o3dwO/gj9M56cLw+v7IsC7FfMekcF0evwqWcWRSsZaw0/BIYOXB+/8LjMfObD99w/+5rhgzW5cjFHvgff71T6pV/79/2pCEcHj3P11cymX1rnM1j+hZdHb+5VJw1tnol+sh577Thebg7cu6F1Uc8QvWF+68e6aK0MVjDgVYzOW8cjytN+kwQxWMyPqWRiBKPKzUXFIcMAXGMOp2suEGzNuchePD3fPP+yp+/9zy8VWrZcfGe87mTN0Esku0ynVa+UixxqQK983bdOAX46kcHnG94TeT9jFdl8ZFhxsyQP6bJOqGL6Blt4JdE3S5znwyoVJZlAaewGetxIW8bghB0RmXROxgDp4GUEuaEXguit5TbwOMZ1khL4vX1dQYNGnAenj+8EJZAkshedtQ6zQu7G8S+0NvA1AhOMDopelpunyAFdQ6TQWuN6DxdKzYGwTkur2ekgaD0IkgzRm+oKOodNbdPnzUFxc1Dw0HrGVHBOUGdYMVwcR48fcsEjfRqn6LeEAKMeVAOE/COrVbWGywxVIgusm8Zld+9EP97d6Zm1kTkvwT+B2ZC8o/N7H/5N31GdEZIQR2tVmofOJ2YovOOUgpOA2UYuITTTh+euJ7YtwutdtxyAqfUbaeb0EcnOEdyAVsX/vLDmasuHJcjo3aQxuKFdw9v2WolBaXXSh1Cbw2XYZgHHyi9cHc8UkrB1BDv2fYLNhoPp4S1V1K85/H0hrZfGFQOFL7+8p6v/+Tfou6vrOsRwSECw4TeDe+VFCLWO6PME33PmSVGbEzgTVWI6mmjz8hyDLxTXAhYH/Te6DhaMXxwmEFr5eNcTOggKL1NEErUITZotTNGveFqDTHoGSTumILUhYO/4/p6Jnultc4xBqRnrtcLbkm4XSkvlSAH9v2JUxB0XLh7OJCWhWLGsgq5dh6PV/7+3/17uPJLVI98c33Pb779wPNmuPXHNBs4FcwyS1ywkdhrpXRwmnjZ4JunTs3w+EXi+lSpY8G64+HxQKuVay5I78Q10kcF6zc8baA6MdWeZ8S57ztJPTKEekuz0/EOpFNbppQZgYHRZSfeeb7wBz5sDfGBvWyIgo8zetRxRMQh2tBRWA7C473jzRJZbEdtw4YRwoL4gIhMLF0mjmju40FpDLPbfVTUOcQpNhqlNJYl0mpm5I3kEj03vIZbEFLAhD1n1nWdaw1h1EGvE+Khd7w4kIFXj9FZ15VSOgNH2TdCCIwBpZT5GRxqbTrs6Ogdus2Ir+557mGZ4+BCoLZMXCL7thGOEzaodR4CqkqVOh1ca4i4eZB7pZdGipGcM4riQ6D1go+R8+WJ02Ghq9EcFOscTvdcrjMY8SEw6iCFhdbrfAZrDBu03m6HqCPoQh8D5yO5ZPbtyvG0ot/Dcv+69oeITDGzfwL8k/+37+99nkTOCyk6ZEAfmRAOIDNdLa3PlF4UjyM3o+5nhg1Kc7w+7ZwvG4d1JaKEoEhssHfOe+fiEz5FyhBwwqiNx9Mdv33/ns7C8bCS/IGn6zMHv6AuUG1wzRvDK71X1AudwaVtDIWQAl0ry6pUe6buitcrf/bTr/lbyTjqhbQoaoWjf8cYDbWBC5Fhnbpv1P6Ktxk5CoPoZ7SoAmMY3Tp534hLmoWCG3Ce80Z0t+hzDIIKXuc41lpvKVAFiQxns3AWZko3HbCRYqC1RhBmusdCyRun45GncSZEBwy+YAWLfPPLX3D37h2rP+BG4NfXv+R+vWdrO0M98XAka8Ge34MZx+WE2UIoO//ujxds/BVtrPz5L35Bc3csj3/EE5WX3eNjg/yMt8rpYeU3H3ZUb+m6Kt9+eOXuGLg+PPAXL08kv3IMCaQTzDPchHyWdMC04EwYpTJEb1nHTEFH77TWuF8XrA/MHNE7mg1K66gMRD33x5V93yltIKbsl8F23mk1EmKEBl0il60R48rqO1UbSEbzxh/95J51/IpVVjwDcREceAxZJh47ZKAGilJrIaC00RAxMCN5d8PBPYJyOr7h+tvf4pxDPxaqdEaD1SqttlljSBMTRBy1NJxzJD8xUwS6dUSgtUbrGZVIb8xMRxzH4x3Pr2e8i0iAy7YTzRE6kCaMMHonrQtk2PKO97d9WgqtV9KaiDFO2ClEnJOJZYZEHZmwHIjmGAN8jNQ88d9SCkMg+cQ1b7dnHDze3bNvF0ygY4ibPmFdjtPhrgv78zNB4hyPMZDeQAUfPZI7HSE5T9WOqXA4BHptYEKx+jv7vT+IM/3/ah+LL0OgD+N4OLLnROudMRq5dOJypLU2K79hxUafTijBry+vvG7glzsuQBfPJXdoQq0d8UeS61z3ymXvvHl8INcLv7q8zoKMZFoVlpaootRZ/8drwLuGuYa3gXRBTYnRc20vvLGBN0G643BYuF4HXx3uOF1/CRrpYeCJeB/Y6xNRHcM8arPq7Zwhw2MTpUI0oC7NU7R2hmWcQEgR4FYoqTQZpLTSyoyoohjmlJIbDmHxaaZKRXARpDdKnUUqQ1Ef8KIMEcyg3jZnHxtu8Zz3mfYxKotEtl4YvXH64g3rsrLlHRROh3e43llDpJonhQB1cPzRT2c1WDq9bXQPh7BQhuLvAn/6t7/iaQ+81BPP5ZlzKxzu37Jfdixnnp6ubLlxf7rj9fWVS77iZeWyeepvBr9sidNReFjhj97dUduV1B1v7hKvOfPcldV77l2jFyZuakLdd4I/4L2x1zFho1IIzuPGdLLNZjp1zZXerpTa2C2xHN4xcqB0T8UQD9oKKXVCUKwaR5c5auX+bcLMeHE/5kFfkTEI2kEnZIN1gjgGHmmdvVZUPc2BE0MHSIfh5NOeaHWAZeLpxPn8Mh2s6yzhQMmZ2hrrcrxhmDsi4AOMAWKGaCOXHfXhEyNF1fA2U2FjQivmOrk7fAyIGHnbcSKIKkVmIVWC0Otgb4boZK30XjETvDnURV7ev3A4HLBmXPOVw+GAEGAop3hPLpmuiotCvRS8U7xPnF9eOZ5Wug1sCMF51GC7DvxyoJUrxzVx3XfoG+Iidf+IITv2OiNVRRniESZGjgbGGBRXKbeCca0dVUNkEEf4nf3YD8OZ3v4eGJfccK6hLoApxmBdPa/blTYcrXckzjTpknfqy0ZuEO4fydW4XDZOhyMOT9CAxgURx2I7b+KRoMa+NepQaje26xWfBN+vvFvuiU7BCiIRmrFaw+cz7vCGJU444nx54u298rAoUTKnNLi2TDwojyt8+ZDwfeM+JoIAvRGTp+Y606RRGNYmQcV5PILGRM4VvUXiMPDqANBbdb/mMtNDZOJ96hmjEWOkjDaLSzIjW6eOBuT9yrIeAcM5P6OV0VAXUTcXc8eQMUgpMZzDe8dl31Fs0m4ceI1zo/ZCuj/hdVJ/et7JrbPcrXMDVuF6vYJ2XFCCX26wwkC8IhhrCvz8251fvn+PWuTxdJrMjeFY00LucyN9eHq50bEAdl5fC285ouGBf/2bjefDyvsXECIv142v3y3kLNQ988ePyp9+/Raxb1kc2J5RDeRmrIeVPgp1FHBKE5tRiwaazUJjv1zpI/PLS+BwjFR1HO8Tz+dBbZnHNyuXvfD4kMAqdr2whsHqHNfXxvO5shxW3oVEvLEmAh4ToUuE1vF+8Lo1TIQUHGE01Am9NwxDncd7j5ncqu87RTqsnhCPdIMiMFad2OgwJDi8BGZE12eaPoT9uuPVox9x9tFmsenGMVxToNRZ0PRBcS7SrlcwP6lnKM7pxC1FCB8j0VwYbWL8HYhLotVMcP7GFJjBUq2ZdT1SSwcZLDGy5UxvDglCGY26F0Q74mDfNuKy3DBTh4tGbYVWPN55gj+CDpx4nA5qraBCiMuMnG8YaG6VlBJl2/HeUWvGqcdqIapnjImxth9qmv/XsW6D/bozBsQWyeedNQVijLxeL4h4Ylop2+DbLVN745gCwSt1ZP7Fz35GWu8QUUQgugg4SmkIjeOyUnPFckac8rAe+e3lBfOwlZ2vjg/EOHgTD/S8cxoVlkgtSji8xVrlGAy1V370CF8/KD4od4cD23Zm9EqKJ5bxQvR33B2PJAd0Q5mRphelt0JaF6z7j/UljJmqT4xUqb3NCiZG73ND5H2fjlQVsUn3+Qj0l1Znqh8mE6DccCwfA5InPWU5HinbNk/tG12o93lPHw+zdtv0rnWkVxhG64270+OsAovMYhNCzTs+BnLZSHdHJHlqb/TWWJcD3Tqmyhhl4rEYMgI977SS+ePHR37yxVt+8/4Dbml8WxtP50Avlb0liipPL2fEJXp2dCmcsyH+CqVQO+y9Ij3QNRIPR3772qhZ8CHw621w+dl7XHa8u+/ouPD28cT5eeeP/vgLDiGg1oBGyYWKch2BUjpKJ8WFumfc4chTv2fnwC++uRKOC94CuRZ++uWBcv4t0UN5WBm50iTSoyLaWJJje/3Ah+0V543H9ZHLVllSoFzPPN4dGBYRVS7nja/ullvG4rDeZrWZCdHImHzW2gun04neBauGekXEcBKgK9ecUWeoQusNLwEbjegj4oS9F3rbSWG5bbyJW9aWseEIzlFrobdGioHVJbZebwe0R3tjf52RZu+VboI6nY7cjPPlCY9yjAvnvM1UvzXMOjFOPNp7neOsSj5fcdFxvFsn3QZoGN0anekk17BMZsCNw7pv5QZBzQKrBqW0eRBEF/FeOW9XDimyBM913whBydvG6XRick0dpWRSiGD2N8eZCgLqCcFQ9fReCcmj6nm9bHgfKUNRHFu+sptR1KMyI6itVo7HI6aBuB54uW4EZ5xL5bAccV54PmfaqHjtmHUO7sCPjicqDRce0FbRmnmmQW989eaB7emKukAeBYfymgt/9C5xWpVFN77UgjTl7m7lX/7iyvV65fHg4eWJ9c0DXiJBoZUJuIcQyS3PggXzVF/ujowxq8fOzWKJ10mfKfVWIR0dFwMiDvWC2IzizQzv4gT8Y2SMQe0ddYHWOs4HdFX6gFEnN9BEJ+F5jE/EODVjAMaklTCM1QWez8+8efeW1gyc0sdgRanNEFO6NU53D/jlSKm3DRcj22UnhEmzCUGI6igGNoy0eJJzOHvCBN78yIF57s+V5yOMoWwl8vQ/v3CUlcuuqDM6id2M0Q5IadynhDRjb5Uy4Hlk1rQQw8LIxlMTcjri+pWHQ+IQIrszttD55WXjxydFeqXdClxbE973IyYHcs708grFcXxMiIts1zM//mrFJaM3z+uWeXl55e3xjjE6VjvL4Z6n530eODVPupdzpMNbNES2rfLrS+FrLXQbvG4bThSjcL8GXr99olvj7vGBVm8Hm3M06+RScR5SiPR9CgRcvwkfnNL1JrBQ8B+pSQMaDSdCsUbPHZfcXDN7xonSR6ePeeB672g1o+rx0YFzbNcdv06Yad83Rq4EXVAJtL7jVSeUIJOK1nolOMdWMt77yeNUR4yRVuYBbZ1bpLri1ggMzk8fSOsRNUW8R2aEgVMBycCNzuYmFap3CEu4iQI6KZ3Yzq+Tn946S4i0XgiSJn99DIJf6W3KV8Yok+HC5B4jvztL9AfhTAdQe2OJkVHyrOqqTaqUD5gp3iW6GUsKcLngw5E6PK17DusD0gNFPPteuDueuF53nAPrmYriw4I1IaqhrUEf+C4kHzhfdw6nA14aY3HUl52aNw7Jk0tGyLThEBq5NlanPHy5cJSd3itPT+95ezoS/IElDN4uRxY/neen6NJHUE9MM6LN2856uqO1TrP+icrEGKh8VAsZKcZbdOJQvSlzZB5AY8yTO8TAaPMgajaopZDSSr9pXVxw2DCGKGJTPzJs4Lyjljr5iaqTbcBtE72+cLq/o9ROPKzAwILDpCPe4QGzqewZfUZP5jzBRbB5Hc+g1YZLiSUkrCsa3KTRkCZskDNRBwd1M/Jwg0vP/MmXgQ+74+mq/Ob1CWPiaCqzuJJ1UKvh3Q0cdJ6XrbIGz+phXSJDKut65MO2UywStGHpDd9sV0LyOIO9DI7Lysue+fnTK/hAG8L98Uv6WKhXh4uDxRuneCU6P0UH2knLgdU3ns4bcRhSjMBgu1xZ15VvX165u/NcrlfWVdjyhGGcNvAQUqK2hsco+84xJepQvCikSC+VFBKIMMKC0WZx0hutDcyB95HWM92MoFBGw92yoRAivVbMJl4bwzyQnQtse0PUUJkV/dIHPoH2eaCLOK5lY3GRkrcpFMlXkg/k7YpoY7teJ9E/BPoNc2ZM0NnM5vrqjeD9nLc+oQVsQgS9N9SB2STQl1I4pAPbvuPFU3PhsMSZwksj187xeMRLZN8KtRgxOlxYP9HY9KY2M5kqr9Ya0m+xqAu0T0otx0CYOVP/pOD6XewH4UzNjNoG0jcihnpPbnniQh3UeVQTKc4TLgPvM6S7N5TaEZR3IfFcO9E5qtkn7KftOxpXNCX250Lyni/evuX98wvaDW3GYY1s20ZUQZ4u/PTNO07V6FH46m3C+oazwd3dHSkMvryPvOHKqIqOxk/e3eNkTDJ/NHoXogg5TyXSYT0xBPAetcb5/MrqPW3bqN4xT8pJi2GMeSr7+az1RuuIceI7Ejy0Tm3tE8G791mYU1VUBM8yo5qUEAmTOK18OoF7N1yYipJ1Wei3axhGGYPWC+vd6Zb+NVodxKR0GnXc6EYqOPOMNtUlgsep0nol18kQGKMxuseHQK3TwbWRCdEx1GhWqKGiQFod7qKIN1JsPPyZ4y9+eWb/q2fu0oHXfXA4LcS0I4dAyYapp9MQ16HAEld6vVDSiWiNJJ3n62DPgeczDFNwC3dv7vlnP/8tarA+etLoOH8iLIYtiXwtvFyUau+IOkgX43Cn3KULvla+PB25tszin1ERHhTaWEDhWQeXHtkk0YfxoQ5iiKxRKd0I4ZnjIZBzY2+Z0jtHn1jXhBuTvwmQ81S41dIZTGURThltMjZCCDOC7o0xBHeTVgbnuV4vk4vKhI1aqbfi15QJj2qTCXKr6ov3qA+UWggG6iOld8Qp18uZ0Rp+SUSvtL6xpEgbO8F5hslMuxePZ7D4hX0vpPWAxkA04/py5tK2ST9LaWYsMdHHTvPQ2473abJXeueQDuzXDceMZGHCIOvpjjFgL1NUsp6OM4K/MRScc+Tr9ab4mxG7dPDOT9pXKZO3e6NpzWDNYa2zLuvv7Md+EM5UVVnigtBvmvcpUTcHMKa0zGVKBnEOx+QMPr2+p/dBcJ5ijW/zhUsV6ovy7uGe5JTltJBC4Z/9/F9w2St/9MWPeHlp7OcLITgGDbcNvGR+/OaRN1+95aBK7y+oXKjbmSgbXzx8yZsEd+nAIsYh3DFkZ4QDrXcIDvWD4TzLIXC97mj0BPUUqzgN9DLxohACtQ9CDCzeUW18J/mUyctzqow6C0XiHLUZISW2/cKiES/f4Z4ISFBqq3OhamL4STtxzk05ngrDMbmtQLNGwk9GgFMYYE6IMrMAlakgcTGgdiNM4xDziHa6GWMYpopzN91+7XRrs2hSCqU27k534B3WKqXN1M/6QDWANQ5uwWrDpKFrmYeAOVwJ3C+RuBx5e/fIA3d88+sPCInGfMZSCrokQjC6DmJ0dE2Mksndke4Cbx8il32jFKUVh0mlvC9IX1HveHkS6I0QBHVGqsq2D7woixsEHeA3/vaDx9HxgNcrwQ/yy5nl7oC0TFqMoY6jVr7ZHKYHXO4QEwX4pjmezh9IJpR+pIcjdVSO9564n3HeU/aNMYxRG3EJU3HU8oywMGpuJAIqQssdG9DbNrHH6044Hah5I+KQDqVccRIJYYU6xR572WZhywltModgCD03Qkycy8ZqneA9+7bPbNHPHhJjCCne09uOXSvdBqf7R7o1tm1Domejs96fqLVR84aZcfdwz/PzM6f7u5uiLTDGFKKMvYBzUCfm2nph2y+ICwQP23bBuUCQgO2V5is2BO+Vmi+oeoKTm9MV/HrifD4TwgxSzEHudXKqndHKlSUlet5JwVH3HVEl9/Y7+7EfhDPFIIUFpWNMh9CkEzmKqL0kAAAgAElEQVRCAPWOc6k49XiVT5pz+iD5SHCJl+09XpXVGxHhqJ3Fdh61UvOZP/1iwfTA4gZ721hU8bazuM7f/fKO0gqPy4ZbHfVaGS4zTMnb4PD4hoMmwhic8xM+3JE3Q1GOxwOl5emclonNbLXhQgCUVjreB5TZ9MTKQE0QP6VvDcNu3EaY6RtMTNOY8tkJ5wi9ZpIP9DYQBRtC/+gw9/pJaipOkDFQ7TeSv6NjnzTIH8H2Oqbj6yaTPoNj2FTKlFpY1hVrmd6Bj+RynTxfsUobfW62W2OJqSrxeAdNHevdPV0avQzWlBhjvqdjOJuV5JkaMpvSDCO4ADe3tS7K4ynz8+fG3jPiD+QilNFZ0yBExxI96IRJrtcLp8OKUCZ3VoTX11cM8CFSK4QQiF7p0iltRj5LOlDK4HJ95eEh4kLExwlTnAIcMKTtLMEzvNH6bHgTDhFU2Ped9RA5v37gdDrxJYVFnrlGY5Fwwz4HD3eJo3oOq7C1ikrDl4pjYrcSFTcmDtpap47ZnMdUqKUQQmLU9p1s1juM8UmcsV2umBmn+7u5Rgi0OTGMWxq+rlM9Neokw/duU6l2d0ethfv7e+q+MyelYOZu2vYpKbU+aKWzpBO9Zuq200bFeYdXz2h2izyVXDu9DUrZ0RjItc5GOXnD+alkqtlwSSh1Qhij9ZtqCQy9sRluhdlaWVkQVYYM8CDSKaVTKROX3QvRB2IK5DylwWow1JAxebnj4564kfddimw3ocvvYj8IZzoQtgZBgdo+EYCvJZO7MDRwLkatO8fjSiWwLB72QlgTpXbu4pF9L7g+CCHz9XFlXC9Et06uqTUWjXz58IZvz2euLZPGMwenHHB89XYlAYf7yPPIvLzuqH/L8d3Kmhbgynr/wF/+6j17zfzk3T0Jm86AOdljzKYSUW+V0mE064gEai04mXp7GTZJ0u5GJ3E3PtyYHaM+ykmnFHKeum10QpyE5I8punMOL7cihBm9GSmun4pTvVdkOIYI3cZERJ2n1j4j+jA3+tQuC7WOm9M0XHT0Xm6aaSMmT2+3PlI2Mbm4rpQ9IzIjz9EH/rDQSyUuCWAStb1SS0a8pxYm5cVBq+OGFRul1rkZap1jIMbr6wuP9wd+8RrZzjtdE8F5luRw3pGC43K58PjujlrbrfBSeHgIRO3c3zvyja+cc2Y5BhiNVirhsOBToMsgOGhtcP/wlkFBgPP1zHoKOGksvpHLFS+zaca6rnOeDysM48uvv6L3yul0mjSyUXi3VJYRqOWMtUpaDng1Vm3sr5fZl0FtqspGQJ1OpVMfU4HnHKPP+ejDvsOXnceFiRuLcXM2RhDPfj3PKGsvE5uNES+zkYn3AT8GYU3U3ogf01wJBBHKXsl5pwNLCPS637ijfWr3Q0TCbCyT2wwA8G5CSBIIMbLvk+EBIDictCk62ToSAjVPlom6ed+tFWJMmDW4wVDOOZw3yr7hQrp1OHOAEmPE49nLDnqToerkEAf1SAdkympfXy83Qv/+SazAbT/VXDB1cGv80/bMkpbf2Y/9IJxpG/Cr18wxKo8hos5ziJ5rqdAWLhfj13tnWR45b42tnAkj8+M3X3DeN0YM1Cqs6cDd0XN3H3h9fqaPwLcvV+pQfvLunjdpQXvn6BXvKj86BQ6r4+4UoVdEPak33q2Jx8PK6+XM3cM9Lx+eOD5+yUtrtHbPGeObl8xP7xPC4JASpc5JUwTKwKwTQkCio/UdG8K44WGqoC5Se0WcZwwm7qnK6LNxiVOHjYYwYNwq+LeCk4Y4o5VWb/w+w9/UULeQ9VOjk1orIXyMSI3eB967m1BCyLngR2aIzu5rLt6i5NnBJ7pICLMCKrc2d8LsZLRtG8F55MYMUFOkdbx3Mz29NeuQm5rLbjSv3qdm3glY77O13RhsJbOdL/i4Uny4adDjLfISljg5wHhH9EyJ63Flv14x8aQUWaLiJHP/6ElLwfnA6OEWKRVGmwU30/1WdDNqLxxOR67nig9zgz2c3hHjzjEad9Fxv75hu15JcR5AwTucgz4apTWQiMZIrZl0Wmbjjb1R48CtAVEjqkOtM0K8UYYKY3TUuckIuLWiS2kqpGKM89AMs6nNskZ6bsDHpiKzIj6s0/LGmg7g5+FZa6fWSifjNKA+IV65lgLSGU4pw1jMqH3CNjHGiZ+OwfVScDoP+F4HLoBZpzpIx4WyFTofs4tBy/tUV/VOq1Mu3VvDqafRpkihT2fZbwf25EkPVBytD9b1yHY5U3plWSKltcnDVlA3m5/s542wTFZP9AGnxhjK6H6uMzEulwvqlNEna0Z0yn9D+E7xZLdMSJltDY2/IQqoPjp4B9Hx1Cp6LagMchHimrh/tzJedro4Go11eaSUwm+3SooHpA5O0XMXhBiUWo1hC9kc19CwlmkNLHlqyYxe+OkXDzzwzOP9CXNwvTQe374hhUipF1QG7w53WGn82U9+yrVUGpC+uCDOc0z+E9n9vF25W5ep2tgrcYmUVmcKr460HKcs8IZzTk6pok2n1HPo7GKjSrP6XWXRAUPIeWKNc3Eq4hylVDzyqWpq9rE/6exJ0G2S8f2tZZqT6XQ/9p5UvXFePVhT9pJxPuDEcKoUM9J6YLTZJ6H3cesnEOa1VJBxc64COWfIbSp4gr9xHQWYxQDnI83y7PY1Og4/q8yj03rHeUGdI7gTQ2Z6WrZB9xCWxN3HXqmiaFy4XneE6fhiWmfPBK3ERXEYHz58YEmOWq+UqxHWw6xkqyPvnePdQhsdNU+pmcu2ISRkRK6vlfcvT/y9PzlhWhHpmFOCfozMNsLtwMG5STfrs6duDAdEjdINDY1DmOyGvRYkKrlM+tJln3hir5kUFtwwGo1lmdrxeRg6xAulZkKMtFFv+v6Jh4cwcWOYh/Hr6yv3b9/SbXZYqj0T3DL7MlTDR2W/bnjvGG3A3rnUgspktZRcSOpo+cYNtkErg3VZMJnNCaNEnl6eOcaV0s7E5PBeb+NvjDG7TpXcwUWGCZY8xQZEjwEpTBHA+fU6NfStEVzAboyUXCvXfbYJ/BgQOIShA/zMskII4IVL3vC6koJRS2NrOzHNqHv2jJlQ0nIj6MswpiamzezAT3ZD+16f1r+u/SD+h3pejHepU64vuN6obbANx3kYH7YLL5ffMmKnSaO32bTj4N1Ncz3wMngIkYdD4ehfcP09bmyk4Nl6Zt8LDMfTtx9IrvA2NL5MhbtD4CANd33h64cTMcB+fiHvF0be0J45BU9rV+4PjlOC47ry5njk6B2JTvJwt0aGucnPPB45X16QBG7xoMLz84dbJxtoQxiqDDPcmibPDaHLTFd8dDgvmPOfdMhN5h/v4qdCj/cR9X4Wgr7XD1XGXDxObhSRAWICqkiYyhiRWzMXF1C30jSRTo+EeABxmFMWF5FmOB8x8Qiz1SDS6TS8KNEHaJ19u7C6yPbyzOuvvmV0oClWhZE7Qpj0Lhy9QadjfeCG4EwnZ1I8iQ0CVH9mpXN/f4/JA7XPKDL6zuGYMD+Ix0BcpiInbztiO3drZgmDmhuUSN0rx9Vxdx/RJAz19BEwiTxdKt9+qLw8NUZNBH83e9S+zNZ0D28O7PkVRbhsBRsDEzcPlLDQdQo6BjKb0ES4lMHeI5frpNp0UT42Ixo+slcYlshNCLJMNVoXxpjNyp1FaI5AwJmn9EYb/ZOM2HXHEGXfG7lVhkCMCwIs6Ui6X+muAp3WKjgYVbAuIJ1WtslTrYLbjYiSDoG43irsw9P3xqjQeyCFE95BbVdi9Dcy/AvQcEEY2iilzKYyo1H3nZhmITWlBGqYB0kBibMrVGkDG7Od5N3xhGhHY6RVKGq4dYEYsfB/cPcuPZZlaZrWs+5773Mxc/fIyMobmQLBiGFLTJmCkHrGmFH/BfonMEVCQmKAoGf8CCTEqBESEwYItURXdxZkVtzczc45e+91/Rh82yxSqESXOkqQqiO5IsLk4W6Xfdb6Lu/7vPrMYEWB4FE1sHV09tKoXTA+gPEM08m10noh+kSvjSnODOuoDKVRHbpdY3Qc5n0E4+gyeORCN39PKtMYPE4GU4hkCVQR+nDUCtNy5vXxBeMeDLwCGxC8BU/H0gghsvcBt42PZ89lXsgdXloh0Pnqqw98dU18WBK+3jhNC+HNQrerlML0wcsfvyWlmeT14XJBNXR7KeqFtpZpUkCGsR6sbsJrVxDtaZl4vD5UQN8Bq4sE7z33+x3nlNMqb7PHA/6slaUcFaR6iY0PKvZ3gxijsjlR2n9IE104nCXy/kuhII4hQ5dBx2ICZw/Umy4j3l66hxLVwxqQ0d+lOfo5uHfbocNQ36EVquErpWKO2WvrBYzh8lG7htPlTDsgHmoQ0MrZG61AkYMQ7yyUDM7xcgN7ulKcoYmlmjP/4l98gzNv8yxL3zbmeabRKcYe3mrDnGZGfaV0rSSnOeBCJCX92mr/EZtfilaJy6Ltau0d0EPLOgjRIfWF3/w7X5G//Us+fViodcdNEWkqTndmUHrGDMG6QC/KOcg5H5BiQwp6SHRjKEMoB2vWhytrrlQZnM9Xohns66aStRjIeSd5PUzEKAk+xghdR0TGHyh/FFxjDjOLMcpvzQc9y00B0ztjCCF48lYJXog+6WihAaMdoPrBYGhbbSwYTx27qjoOvWZDiCnSi3DfVqzxTCke7FgdBUkTpmmi5IoPjlyKIh1joozG6XSi5QJmYK3DeEctnTB51pJZ88ZymnTZ+WaV3jO9ZPoQhmjqAlgFxXvVlhr0PZSrSrb2soHXn+cYA+s9rRdd6B7jpz5U1qddwJ8hgu9f5yWjU/NObR68Yw6R3Do2RW6b0oucsepr7o+D9hN4WqLOSXqjTzOWZ2recV4P2hiEX5oZ1xt9G6Qlk0zhg49kORie1uGDZWyF/vLAfIhIh+v1mT4K+77r7WotuRTO05k9rxTbdXHk3lB3gdbVyeVDovm3xZDVxdEYR3s2MKrGoddBsJ4h6ohx3tA7x1Ir47x69J1TFeGw/MmcyfB2LL5xJNWG2DHOaeXr3I8AbVEVAVZnmNb543MxiGh1ZZ3DHAezOQjlwekD33vHpqDStdER9MAOqKzGGsEFD9NEBIyL2N7Vc28E5y2mKxW+tM6Q+g7BRhq4C2V54p/9fqNOZ757/Ybf/vrCp7/4ms//xwtuEiBQauXsVNIlkglBwTPnuPGrTxdq2fnhvnE9J6b5RG+DtdzBByqR1/vOfEmkFMl54+krr5ebDLzMrPdXQhosF8f2+IGffXqi0alE+iacQsI6z2O9Hd1ApDdLbp7bdoNgCN7jgr5pK5bS4XWv5BCpWXXEZuss84lkVgWjx4lSK6VWrLNUdDmnhCZddDoMRjrRz7RRVUWQdKbs40SpO3TUSWQ9ozeFWx9qkHmeud9eMAk1cCAEY2m14I2lBSWsFGkIFR8j1iVK1iWjwVCyMmrHEHLOlDYYtREOTGNtmeATrQvJWqQ2vHhK2wDL6/YFsARnMNEQUmRIZsgguIibtLvqIsxxppSGGKu832BorerOIbhjOToQY8AOyqiExdNLJ54Co9sDxu7oVKxxBJcoVGpXS4s1Do99XyT/lNefxWHaxfCHLxtxOfG1bXijQ39JDv/I1FEoY2HQEQvROwyW2+3G6fwEzlPrgy8VVuO4LIbz6YLNOzZOPG53wOLszPUyYXrFjIxxhtNyodeN6XnBpMR5uWr0Scl8vKpmbV5mRcqdTkhpLHPSCgF1e9RS0BwpyzQvrOtKdDO9Q4y61TbOIwhD0I1tqdjoqLngokJdetcbPoaJOjrBq9VUpTdnFI2iP/heK3GaDiKO3sxjdKyB9iZ5OgT1OEsvx0LsLRPoOMSV8wm9CUNU5xtDfBc204vGYrhA+xNwBaKSrLplnEDbdno94C1G57xiwPnIsByLsAANogv69w21mIqBtXv+p3/+Hdt2wWyFX/3uNzzaCjYg3rO1jcspES+Rsv9AWhz/1nXmdEqsJfMX10Ea33P9MPNl8dDUndO9RbzjRQY3CQxTWU6R+/3O9WrZ8s5yXbi/rIgZpJMS57cRObnEX91XApFeB33AKcIyEqYZAnD2IGSa61QXWKYTZmRkCLl1nBVMHzyfJj7vldIKMjrXmPBSAUvJO36e8F6r7NbL+0HaSj0uUBQeYwzr/ZU4TcyLh1GPiyngBnhvKGUnpQlGwwXtaByG2hpxSswxcG8N77wuOYPHBIPUBgac96zrTjCeVjqX05V7XjFeeRB4XYRZ68nbyhwCrf144NbaGAMeY+BiorWO6YPS8vsz27tKpi7pQsfgnOU1q/bbDIPFq6uOI2vNGjBG2a3OqBpkqO5cWabKmW3dgrHUIoi0QxWjc3xvPPt9J50SozSM08reev3+/NTXn8VhOgZcLk+0kdkITAd5fyqdqQe8GDD10CmCt5Z9u5HmhdfHnaenD8zSab4jNrD2zl5XknGUkfn04UKyDeGOBM+9PoiolGK/rXz48Mwtb6QPzyAKrfXRvUNGzFDMWKsNZ6AdBxND9OFzjmAdxjhazyznhQbEkBAjCrd2TulHA5zziB0EH+nSYbwFuBl6V+lUCIn7bdNDO6h7w/qA9UrS8ilpderc0bb4Yz46cL2/g4ald1o72sQh4LQSHa1hRDfzpWf9erouzABiCDpGaAMRfZPK6LQBnYFlYIc6c1puhHSi9IF15rgI0jECabqowtO7aNZQPOOdeT+kuw98XoW9fWRvES+V3//vL8RTpErVyvO08LPzzm+fK9E4rFgsnVpf+Dh1pm75dDozWuVXJ6uWR+/Ya6WK8JotwzierolSX3m6RJWb2cDjdSO4RPSe0jJDhGk68eW2ksKZWwEZltIaN2mYR8OJx/aNJ7/xy5/NOA9TE6iaGWW8dlOjrFxDovRNN/0C1+cLrjV8NNStEEOgNSXDv0ne9GencjZp4IPFWHBisUnlXpYj/+uIvem9H1W2kPOOcyp5MkZwuKPdT7zeNwWxj0Kzg8vzlX3fCelEb40hQjo/KTgETxXNK6ut453lsa3IMER3LG+apjkArFndVzmvcEB7mgyS9bjjMp/mSKtDRyV5R7rQm1VJpHTAM02TzkDnCYZ2SW0MpNaj0tYlXWvtkJR1rFPdrKpXhNJ1ccsY6u5rg+V61kPHv6VRqOPPur8nCD5nYToGQsEJp6gRDV120hSZTCC0TC6DBjgG02lmeM++74yyap6MXQmm0KulDYufT9AqL1++5TxbagB7mXmsBZcM1ib8Ids5L2e2dYeQ8NZgh5JrgnXs6068RqCTpsCeG60oEZ6hCxVGp6PzmXbMBbVq1K9xGMCOw1uvKD85PPbGqirAyKAPzfPRDCidUcmhh3NWFxpyuKVaa3orI5Sc9SEbXWNO0PgI67xGPPRxoP3ssUXXaqDkgosRO1Ra0qUdaaDHTS0wDuivtWrNY3ToBmM6+64gmtu2MV/PbG1XQLMRRu+8ZVkFG1RobT21D7aiulKLYQzhy7qT4hO9DprGtLFuevF4ZzC1czaFTwGCFFJcKH0wolVKvwgyChitsL23bG3jPJ/IpTO2xl4qBMc8e8xw6sLCMXpmXzt28YRkcHZQXj5zeT7xWFdkeNa14JMn750UAi8vNz59mlmiumhccpwcRN+UZE/HRk8XbaNbqyxB7b+jFRZrKdJw0klp4X5/fT9EvTsuT+eV4XA6s5dMTIa9NiaXqGVjcgu16gpThpojvHXEMOtB0zPLMtFaYdS3oZAFsWxfHsynBZFBrU1jQpzHiDJU3dGduODJW8FFw3I+ARCnhW3L+DFw04TtwpCsF4ENlNx+nGUGz14ye1EZF2jKgTWeaXZsa8ZaxxQmnAxqXglHllQfjTE6zqgRwLhwxIsMaqnIqDjvsV2oA7DCPOnY5r7eSKcz1h4zcudYloV1u6v91WhOVAwavfKWFfZTXn8Wh2m0g1+cd62aWiU2ISbL3h2uNuY50eyOXc58eSRCijQqtypclpnzOWEduFXL/egMDZWUODv47e9+ycJGYuPl++/4zc9/QV1vpJg0uMsYaqkEY3he5vc00jqKDsnLoJdCcI7RMnNy9CaHE0VjQprzeBcwqDuj966xC62ACE4M+15xKdIZ9FqQNgjW4bAaP+ESA8il4GrGBq95OwziMtFrxnSr880AxnrGMespu25qMapFTUlbGLyllnYsxTT5MnlPLZWctaqpW4EgHH4abftkUEohxgnHwMXAut7pUW2AMuS4SAwlZ5bTlT4q0zwTXHiPwRbASuL+8kKcI8NMvAyDaR3xYM3EXje20dV88bRQumF9aRqXrSJVeoWXLfAYkUuI1LLhEoQhuNFJIZK3VVtiM1S0zsyeK98/Cs5cscAULPWxYydLDGq7rMZg7eD2UhAzWBbLzz580Nmvd6xl5Ve/utDJPO4VyYNTiES5cwkWbwO2PJiDo7eKnVSj661ujautzPMCWB63O8/nC6VlUpow86C/5RWh1lwRHc/0khl1x/TAMgXK0JSEJoPJn+m94qPTyJEjQNBZRzdaiVnryKUjAUKH0Qc9V5KzjDmy9koMiZazaqElMXIjzQtiPXrMdKxTJEiIli/3G95HQnTk244V4XG/471nikkdRrXh8Gy14GXQ90ac9eLc98IcEzjLVgsZgx8dZCeFBZrGL9dcCNFAE+gVKwJ0ljmScyWYoLuGvWBTxNCYQqI3LTKu16sms5auAX/WcHvcUSqbLmRtGYiLOBv4O+jy/zwOU4B5nhEGYYm4I7rB9c7kI9YJW/FstbEsJ3CWx02YY8Q7kGJZe+Mck0pMgmPbIS4L0Tpu94qfHJ9SYjk/IbcHy2km2KCyk1GprXE6L2x5p/aCCxPBHqLp4xbDDNV8dp1nRT9RikbkYi21FWRAMkl1eU2dSd45em14H4hhZrs/sNZp2y6Dl9uDlGa+3F+IaYKDNLXvO2LURtfagnOBFBcsai/s466CbKsypTEE582PLd8wWH6sdId0ci5UMe9JkqeTJhiIdHLXN6U53twxxuNA9cfFofMs6Z1WK1bU7jhNygyI8cc2FbTdt9byevuB6+lERTOqvHWY5OlWIzGMDTx9eOaff7uy7y+IPZYmziPHxrfvhh8+D/7nW+Hf/CX8Gx/hJI6Sd6LT9EuPUMqd0sD4SGma4nq5XLi97nw4T4QZanRUMbSWETuRFo+NXnmge8YHYe8bpWdiCJxmNVi83jPnKWFs53q1XIJntplgB3GaoRSCT9TSSWFGc/oM3uoycN9X1TX3rmmoInRjsc4So4MjEA/jSMniRsD0weOxEnoCrxWcdMtjfzDNHhHHNC06EvBG0zmteumVqdDhuCRjmlQxIF7D6KyK6633BL9gY6L1jXXfmUbEgWaSnRIhOG7byjLNrOuO0IkpUWpmupzeO7jhdDmaD+O/WnntETMt6mDcKyDksuFsPCy+urQyPjDHhLUrQxrGarcWU4Q+6GLBRjDgp4AdugiNKen4aqj6BSAkz8ibVtfrTgyR0TTBwHuPKQp8yTnjfPrJZ9ifx2FqDAZdRrRa1EFTMsEI7WgVo11oxtHkjrDgLCwx0HDUpgdIGDvGZKQKySbK48aX+875fGJkcFf42TlxDjDqwE3KVRSPRjYOoe4ry/Wi8pMDvqDRwJUxCvjEvisNyqIOpGU5seWVaZoO36+OH6z3HAMerFNPc2l3HJaSM4HAmlfGgM9fvqgdcl2V03q/E6cIYrAilEfGuYaxOqtNMfK43xHXqShTMk0zHKR9c4waWtUZYH1kwpTwzuDw7KVxPl94PB44r1k926YP3igV49XCZ71n2zZs8LrxtjoasAx1x/SB94bzsuhsOFjVAu6F4OPhbjHs6PKibhs+QOkZ6xeMdWQ787/+b39kyAeCT3RTwGZOl4WB4XbfsM5R2sTWC//s28JttyyLY2uGq1h+efUkb2g0hvN0kyhu6Ax9W5nPJ6Y48WXdaV2ft70aqhS22hgY9pcBGIKb8AmcaMBfDIZSDR8uZ+r+4KuPMx9cYW6VeWgi7ABlbwK0jljlRwx7zK6bgridsbT3eV9RiZ1TmU94C/3rjXq4d7a9cLk8KX8hF4Z0OioPstYyBFoF5w2PdVVYi41Y62i1EYyl7lkjaozaKG/7jnWeNhrzG6pxdD3kRHACkjPDwBQcfWT2W8fHoFCa0vExkstObwpFccaTTmeGG+R1U/9806iU0xx0vFPrkTpqcNZzOp3xqHa6dpVlGQytakbTW+JpN9q9td5pWcHwy7Iclbpwnk8H+czirDqqcs7HJl+tt3OIpDCx9x1n1f+vJDb9+dT690Rn+majM0OYl8goCi0wDLwxx4YSrDQuy0ytkH1nqysYz5SuyNho+xdO54g5CPHx6YJ8vNJFaPnGd/dMbxu//erE5fA0+2AVbHxs2I1Y1vuD89OJdduYYiQlJSgZr3AEBTHzTrhXP7I93Cj6wzTmx3x2sMc22yJt0FrGm0DZC7V23FEFzvNMvj1UvO/UcrhvWRdUXqUo06VTe8W/SZ+6PuTOHe4qoxBnZy39AOC2WklpZlsfpJR0q+8S9/tKmmbW7U4K8dDUZk7zRM5KiVIdamKYQT3SAqy1yDHm8G7SxUkpuvjYCn6JGCxdYH9k0vnKKpbTYYPEDPYx2HNFCHx332hDkXViu/ru0xnB8biv1GbxrjFNSf9uF7mbxGve8ekZI8JfbytLCIRgqQVyNXTniabj5yv3rIDw294JcT40jp6y7lijb+iPX3v2W6aWlfvNcl4iLWuuevCe3jaM6cQYsKWwxIDtb26yfpCVBrrXKIizyPgxyaBXWKuCs62DYBz10N+mFI7BusGFQB7tYBdERIzaN6vGINfRmZbE0CNcSUxiSFPEGGHflSOAOMq2s0wJKToeaEaoo+DNhHOGfV/pdPW9G6MypKaSKu1IdD+g8mRDGwYZg5qzRkp7i/TGNipiIO9KvDdDQ+0UjoJaO+0BSxFdys5zIFCH+oQAACAASURBVLfKvJxxCN4l8rbR+mCvBW+cdqhe0x2M9Sqxs568V+brGSmFLo5SNEXirZvyPjBI5LLpbNxqzJHF4I3KIvNQZ5X3nseb8uUnvP4sDlODIeDpZtBKJvpErgXvIj4E8n7HIHx6umo7bTTzZ4g6Y8iZ55NhPs/UsmMOi2AU9IYbwvV5huK5JMcihq5rI1JIRGOYrlcejwd1DOYU36u0dd/VdmZUMmTfHDsHFX9ZzvTe1VPvrXqi6dSqLfNwwhCHe4v3ZRxibm0xxHEI+wN5y/SmmLx5nnHJ8WFaaEUDz263G1++/ZYPHz7hMLQBPi4Ka/ZeYbci722+c45Sq8pThpDSAtKOgXzQtNWhkqVciy6WGORamKblXeDfxtDloE+0Vtgemeg9QxIuTOTaVbOL0p9GK0zTmX3XyuBRhG8eO6dgOJ0C222FtPDlrt7x4s4KA887yUW8CYwWeZTGvqOItg5ZbpzSwvCDzTwIreCnwG2vrHaGtTEtge2xYp3n46cnflg/4/vgdat83oUQFx53g7EZa47IDlFPd5o3fvnriZYrdTWKoJvOGFGLrUuqjf3L33/Lr4Nw+mRJNigAOQRq2wlJ7ZFvBYJ1GjNTcyGkeMjKKqDfe2f0azMHS6HUqj//tJD7RjWi3ZOIfhsMLMusaZ+jKODFWXquxDBrekOYaFVJUQD7XpTdGzzeOlpQC3J0erG9OYPM6IhB2/7g2PKOc5Z5TGSphOEJKRKdxoy3XGhHDFDuDRcS8aDsL2nSSzgr7D0Gfd+oa8uS4oR1wrxMlN6xQ4EjRqAcHn5/VOq5VYxz3Dft/sQapjCxrqvOYfvOFCda0zhsBUV4toemBgSfWPeHgm1aJdrwnqgaZ81ew/70oemfzWGq4XEamFdbJaYEXW/AJUWGtUhbOS0fKSlhXk/6Zuqd56XjZeA6GOtIyxURYZ4cdb/RG1wT2NnSys7eGnsZOBuQozIotZCeLthcGUYfLgQVojuLdXp4jaqaUoOlDXWteBfU3YQ5YibUe++MIcza3uxrxnTDdT79CPN1Ar2zTBPbI9PLQAasW2aaPaMOtrozssE6Ra+1Db4vX7h+uBLmGeMsLk1ghLxlXWCMdqSNNnxa6EMIMdG2ijPH12QdoyiMpJSilPMpqcvnUAqogiAoPehoh0K0hDRxe9lorfPy5cHHT8988833/PLnX1PKekRsdGoRXl+/4C9fU6pnG5FXqbz8sOLnidddOZ3OD5azx/gT6/pKaZlztJiDShQ81NUqMvBacQmmMFG78Lg3ml1U6N0DXx4V557ptZNfMjJOfLhETqeO65a6a2Kp8RYrg1I9+1YpUmljYbsXTjFwfW68rMfl4AJ//PIF5xxL9Eynj5yWB71nuvQjqK1iHNS+48TTZTDqwPZKO1xTtT0AYZnOGvttLKMXvFW+J1XwBupeGRZKaUwpEKJTwhMG8RoHPcQc1aHCjhkceEdLyYro27cNbyLDWRqG4A4yGR66oYkuEZ13BG+obVWwDMJWFQTunKV2SNPpiNBx9J4VECONMHl1yt3qe7LqHJMehmKQoKMv6erIczEx0Iyz0Spdii7G1s58mXEhsgK9H3KsfeMUJyqD5XzCW0M8usp5SZSmPIOyq/Pp9rhzuVzItWLc0O+xFK7zhS+PF86nSNu7qoW6sJeMWKvmkp/4+rPw5hsLtSl1aYzBNjJ725XqDgiOczoxTQu1rZjx4DINztPM5bzgvefkFowseDdjMThjNSTscH70JgSniyQTT4TzB4wPOOPJfWBjYmDBgbMTwSWGGPw043zE2si0PNFMwk5JM3UI5N6oQ3SZ0jK3tuFmrfqMN/RcmXwkWs/Jq7Zx0BnO8Cg7TjQmxXZhtMrnly+YoIfy7fMrCQumMMbg48cPdKnMk75Z99apMmh9o9VCMDrv8kZHDoLmaUUfGE3lQnVolk8lH6qDSkyq4y19p+4Vg9poa60abWGVxeqt2iXXxyvb7U55uRMQvnz/HdMU+cvf/0swnjRd+OHLF758/pYQLff7N6T2IMhGIHOZZ2r2NBwxnRHbmS4enxpPX515/njG0Ol1Z/IWWzUR9MOnZ56nhZANo0NMiTR7vN3Za2FUoReHER2x1BapPvD9Wrmvwvb60IrONi5T5BTg6w+erz8FltnghkavrHmQWyKlxJTU5vu8OH7zceJDrDxNK1c/MKbTxdOaR2Tmvgv2eCZ0a3yMAMZQdm0dGHHkutMHyi5olrxVHred3Ae5HYmhHlxIdBm8bndVgXhLHBZaY+Sqi7xjdhqX+V2JYUOgi+V8/oiforrrnAErlLEzz4lad7ZtpdWdOB3WVZdwzmOiIc2ReTkzCMSw6PNjHVvZGAZqOayhWFq3zKcLzgWGE+ZpwouOdMIykc4LRK3KnQwmF3ncXgnBEY/InLTo8q3WrBdLcLyuD2wK1LFTa2WZNTDS4GhVI2zCgfab55k2fsQjjtY0Wr01PJHWd04pkLeNJVl8CvhlQkLAhEhwf0+kUaN3Wll5C4jzLjAn5ZA6p3bB1tqRhWRY73ccCSeCE8d1WciPO6frhXVdkabg5OvTQjWdsmka422tWHsQxg++aF3v6mw65jh2DATVFM4hcnu9k5YTG5b7fVdMWM2k5ZlRMqdz4vG4v6cjjmIoTSvAdVuZvWb5ACo5Au7bSjSBFD37unP/cmMcbeH1euWbb77h5199QsTwzfffsZwudIT7N98Sg/ryjYhu6WtDKblDw8os5LwD9ker6AE1Ka0Sl5l937CgLdm2q3QlTMcW3h2VjtoZlb6jBKht2w5Ib6b2nTgvED2jD0ZrfPXVV2AGWy24oGxRcY5rSBjxpCmR7c6L7azbHeLM6+cfOH260pv62UdVSZlE+2O8iwxssNy3O1s1GNtoeDxCiBEwzMlzXzXp0vjA5CO9Z1oRgreMBik4yrYxG48dR9rlqARn+PTxwmPVttA43UfqZeKZYuU0BybTaMfFWccgiJKSugzasLiYqK0Qjjhl5V6LVm5GGN1QZWCy0NrOFGd1qxmLD4rpM9YoflE0Ytvh8CPgUFpX7Z3RzZEye6QdCHp4RMXWueAIKBlKRHOempYKTOcT0uXQfFqsEbacsQziPLFtm7IUvFaQLgbqlhEGr/cbPkacD3ij+WjWasdgUTC085FaGkP6of01WK9s3EHHC2z7EWY3Bv0APyO6q22tEaLVRFEHo1WmlHBVU0kBNeSkxFY2AOagwZutDabo31GRvXeNSRkKiRYO/XEt+BjYR9FI6tHf43d+yuvP4jA1xihnMgamOb6jyayNDFGJ0DKd1Dc+hOsyY3Lhq+evaGXH98zyPOOcEC8ebz37PqAKl3RhE4ezGvm6zCcNnauV5eTY1htw/EBRq+cjP7BuImMw1yd+qJ3XFwVsqKvC8sPtwSnMfLkV9iqcpkjvBWMc99cbT5cLU0zUrdKc0u1vtxuX5cT6cmPtKNVnDGQog/E3v/sttTc+PX/AOcd8vjDJQponpll9ym3vzMExqAfizrHdVtKk0dF5NNWfjoFzKvlpTQnjrWV6L6QpUNedMM1474lRZ8StdXWYeY9PiZwzj8eDoEoTtbY+Ctu+8nJ7YT53rumZH777zOV8xvuC0MhdmQZPTxfy2vnq+Qy1Uz7/EeJg5IwdPye3RJzPrFmYUiLvK2DZ90onMkxi7xrH4oNlWgLBe0RUg7rv+2GBrTif+fCzJ2ovzIsjRsueG2UbcCDbzlPCFIdUwdEOHKHyPretYPugbjvnOR6OM/RgbYF82JqXWU0dBkN0imFsvSJEXa6MplAUF+iHs035rYM2NAnWGkMI87ujj+gVOHKI08Vpzrx1kT0XrFjWx66SHztwwes8/AB9OK9L3IYe9ANoJeNtQIzBBkcIltIbPiZk71rBWquLsqaAm17qu+rgzfNeelPwrLEaColBDOytayjdGAxp2INwFm1i+EbfKsEBzlDpKo8yoki+YzTSWqMMtVrXveOSwsSDDSAVazwWyHvHWoUc+WC5d3UgrnnFitBQs0LtFe8nvcRFQek+WLbtofSrqgF+tStVLYVIeEsl/XtzmFrL9ePP2PKu/nOrW02LMIWkjiWn7Zs6cSxWOgldIkzO46xh3++HfQwuy4yQDhrSjksTH2zC2s45BO4DymPDGYccw2crMF8uOCfUPnjsja2/sHfL5BNraXS3YyokH3Bz4rsfCoaFbc98dQ3U9gqtM7aiN7VzvNxe+fT1z3i8fOH728b3335HFiFMgefLM2mZ+fh8YX66MO53nuaJ2gvW6EEXkie3ig2exU8YqsY/e8sowhwTMhqI2klV8qHBaDlvajUVIYgh5009804r0LcNq/eHRKXmww6Yj8WEAbHH3E8zyRXHVonWcPvhFc/b/3dlOT1Ra9YNs3WEYzNcfWf5+iNLMJjbK3I+8X/eHS8D7DCsW6EL1NbpNdDNqi6xI7l1jIIPkX2/8fz8rOOJGLHWEGIgOMEGEG8ZuYD1fHWdqU7z50UEqZUqnWIM1zTRWuUUEmuu9L1zOU3MUUHc3atmdTp5qjPaVrZBB1q+8xdfX6hVCwGXJtb1TrAK9cYkWqvkDmenoYhgiE41v+awLvamzM56LJ1GLfiglZX3Hh9U54wMpmU+HG8GaYaUwrEIPSo8JwzvKFvlHJJCj7u65UppLGECCn2tajMtaiF23mKKAsMRwVuHdEjekVs5Kv5K8EHZo6WSWyMtiV419sXisNaRnKP2TvAGGywpqjb8dLlwf73h4kTb1YxSW8P7iItOFSvW6hraeWopGJJqUJ12OGM0rI28vr6+a3Zb1238no+vpVc+f/5e0weswUg7iFSRx+0zKaV3xcllipiuDNm/i2RS+FscpsaY/xr4j4BvROTfPT72EfjvgN8Bfwn8xyLy2agH8T8H/kNgBf4TEflf/pWfhUDJHUdgmpIKkJ2DVhm9MvtJEzpDxDn1+HqTqblwOi3Y0VnihBPLXgq5NfpQLV0pO9aJzkseK0s6U0fBSMUFy74XHUTvRUneeQUT6GMwmYhxWnVuQzAmgJmwKOqrPDa2AmIalyVi7OByufL6+bOmWUnnftMD/rGtWBdw1vLx08/JrXL5eNatZMmcz2dVMAR9aL1T+ZWRwb5tnM5nSi84BvuedVNrhdoEZ94iOzaa6dS1g/U0IHiHdxZjBnvRNnaIYRjFAzqvNs+0KIg3SuTx+mBZFvVRTxPff//9QS+CNCkB/3w+M0+ecD2Ry8b1uqj2r1WCn8B4GMJyjZigl53xilM7zzPeea4fZ142z19995nzpwuv94yMQC5C9Bp4p2OGAyKTYEkTvT1AhCFVyVAI4jxFui5UgqcJ3B8a6W2PWBfrgh4uvfOHb+8kLyyfVG714fnCuu7se8Ybz1Yy19nR8kodenl9fJqxw1HDzB8f6zsSz1tHzkCuzBaSGXhpTNPMum4sMaoiYjQwSuhyRrFvOSsB3uKQYTANci/aqjo02fUtpgNh3zN2GLxVX7ladxqldAxaba73De89yzKxlQxezSYqynCMVphTwnhDXjcNb2xKUQLtTPqoYAfDdKL3Kq8LQVvk5EnB8pJ3pAFYMJpmOrouj53TcVKICRmd6XxBDlNIH4IJlnYYOHwwqjfO5d1S29sgUzD+uAjHkXprI1u+6+8zAkaNB37WS0j2ndYqYg1jdJZl0XiUaSHEqJ3WvFCGYPugG6PA8fDT68q/zZ/w3wD/BfBP/uRj/xj470XkPzPG/OPjv/9T4D8A/u3j178H/JfHP/8Wn4iSvL0YDRo8BvFWF47U0mk9M8/LEVgXKblgZLBMiZobedO5JcZhnFMHxPBEZ6m5Isawl8KWV8I0U3Jlvpw5nU4a69GU3Tmlhcd9R4KCGrb9Rh0BGxzro3IKlnOaGSJ8+PSR1/sLISpirxbh/HRV+pIPPEeVRL350EUMYV546qqpTNZjFkPe13fKkrWWfOjy3EClWvuDaUowBmlOjNbZy0aaZlqt1FrJOWOTO1qcqG/cVpRF2ZpCEKzqPwcNDW0QjHXHDM7yeDzIOfP8/Py+6Y9RHxOtJCxSLd5dWfcHuRZOT2dl0jpHckr0j1GzoMQddkvQoLSUaAKThTkWYivEX1/56/sXPj0pZf2juwKW2/3OeUbjO2QwjG5snTVcr2e2tzk6kHMnJI9g2GolikO8oxkIVjf30se73TH4E8E3tu1BbUOF99ZqVdkH58sTYWxMyVNujY/nj4yysbiMsYbmT7TWsSJsrTAvmrx5KzvDDD5Mnr1uWGeVJxECJdeDheqOiBuOsYVoZHPTf7oYNCBwy3gPIU7suWr3INBLIe8dHx2tvXVq0POhuijjaNMHxlqWZaY9NC2g9wFV00RPp1m3/uvKNEUqnegCo0E3yrPNozD7CYuh7Bk5MsdeX77HT5Eh4L3GNls7mJfI43Ejusi6rpycRaojU/VZ8wLNqHb6kEHJGLS2YYyn94I/vketF9acCTZh0PmwXhIL05R4fb3hnGN6mtl6w8VIOkw2NmjGVClqsDHOkY9z47E/SDbRxqDUqvPj/y+kUSLyPxpjfvf/+PA/BP7949//W+B/QA/Tfwj8E1GR3T81xjwbY34hIn/4V/wleFM0z2Z0qjT20mi5MS8B7wxTUP2i2QyTC9yKwYeTLly6MEYmHWxH/DF33W/U2onXhZfP3/PzX/5CZ6XLwjAwxxkbzLFF1Laxd2ErhpuBvg+23IjhRHOeLQd8mNj9wMiOVKGjdPFhOo9cWJzD2XikjFpCDNSa6WNweX5if+z4ObKWFUvQ2N3WCXHh8+1OCjDCIBoFTRhv+e6H7zQ1suyqBKgWHwMpzZS9EMNEkUE4O7b1lZjesoMghOndLue9bveNNViJ+GgJUennAPvxZn9+vtJrQT17DrPMXA8iE02BE2kKfPj0ERHhsW2EeaGNQRuDc4wHXNsraMWqxrCVCqMTxGJdx4pldzu/mAY/mwx5BKoJfPOauW8F61SX2M3O9Tyz1jvDOqQbqilYhyIExTD5hDGwt4opHXwgy4A2iMlDh2ENZkCKgVZ2Shdmp9KvcsyVk3WqkqiVJTZmcSwfzuruksGtVIgTLXdMBzcKyQlBdtIkFBnMIVH7RkqBVtSx1Es7stpXBEuvg2VZ9CARBSi7cPBxvbCXXVv4pjNJ7y217DgbGM4wqGQ6hEYRB74QiJzszB++/QOny5kuEJbIvlWssSq76h2bHCla+nGQTYsWBtbAvmbEdFwMbK8r85LAB3rNSrWvCkR/Pn1gLXeCT7rwtA0bIy+fv5DsROvC5fKkl7EMcJ4uRRUIVh1XGh4oCAOr0WWEEHnsK5MPROOoveGD4ILKGJ2PjKG8VxMi3Vk+52M0VYGw0G2n1cIU9YJLKbI9bsRjsxhtAmPYc+V6OdHqYCv//6WT/vztgBSRPxhjvj4+/ivg93/y+/7q+Nj/62EqCGk6s+0b0pQbOseJMSn0wdGhNEZrVGNwJ02obF0ftODSAVFYWc4neq+MrrM28Ya87Tw9PbFvqqf8+NUHeh3kfsNYz5YHrUW24iBM1L3STNS46fNV2YxfvkA8I+msXMhTwtKwzmgbaQWfNAeKrrlFYwxM8BpIN4TcKmlJrOvKXovyQqOnufDuc3dvhCfvkD4IMTBdTvio8iYRdWDVpvARGx0+WtzkMA7m0/n9Yth7pXd5J7DXIrQGy5JoY1fQcxOkDXJT6s8oGuhWRj0O5MQparoA6KXhj+TMgXq/l2lSjaFVXWZplVqLqiSsUXSb91hvyLkxT0rOryVzCp7WK41Ecqr/e5oNUwrcS8P4RDeN0QtWIpOB5kBy1RnkEYc9xorBcDp57tYjHOJtL9iD2+rRwyqkRJ+EduQvLSnx3ZeHIhSbSo2mlOjsZNvRRAuLnRNXN3M/YrV7/UxMlkvyTFWpVZXB6DvRDCRr5TpaI/nAvmvmkcOB01jjrVZlGnRoo9DtwItjaJSC4vVM1K24sdRajkVXp6+FKo0pzQwbAcfrywqtst9viPPAgpsiwTrKvjPFyOujEUOg7huYRghaIXrv8TGwbRlnHNflpAoLKfSx4bxnniasafRWMDhutxvn85kmnr0MMDqWCClSxeHSRBPB0kCEdnBtcxPSrMWLDEvLmdkZRs+co9Gdl/cYKzBHSu945xmtv48DyjD4mCjOc3+smtyKsoGn5UwTQcKgYZinJ4xUHuVBr4WQJsIU6NIJUYFCP/X1d72A+ptq5b9RDWuM+UfAPwL4+c++ohQdMJugszmRgRFN0DQNfPTENLMd1Y23FkEzjoJTruFbqmNKiXUth39+YEOiNCElzxiZ220lBI+dJl7WRm6evRod/otVUX0MiKi+0zjL8+XKJhYTvGIjHCBeExJFsLYcSZseP88aiubV8bTvlRgC0pqCNKLjks6aHGCPWFxxXC8XulV6eDAWe9DMY5ooQ91hKSTKnrEhsrdOa/q5BGcYw+B9wgpYHwhGk0CdPaj51mlWea2IVT+/eUvGHB0O0pWxQmkd6x2MhkeZlbkW0jzp7PYII4vWMlrRz0NE23mj0pt934mnGTGCGKEf9KkumvMVYyLGyJoLWQL/8ts7p6dnXktldkKwmuk+cMTgEFEKfbSBXAq1g+udaC3BadVtZXBOTkcufWjFKToqsKNT9h0fEzTLEp9Y1xeW0xNz8AwfaKPTnbCPwtV7TtZQW1HNcVcGRLKCk4FNV5J05FEZS8P0RvCNFOJx8RmcjXTbkTE4zYncGyFN5DUfdlAh13IsspxSpmomusBWNpybKG3nLTe+y3iPkWkPpf2TGi1YTqcTj/WF0SqnOXJ7rMgcqWPgpiPlYN9Y5pm65wNmc9Ksqf2ILzEqtbJeRwhDB5uEqHuLvGWMVE3GnaO6qPqAVlmWRJXBPCd++PLC6fkjKQSllZWNNoTglfrE8WyL12c3RIP0+iPcRESF9NYonrKNd/nfPJ9Y1514OnHLnYoWJa/bgzkcsdhYsAYXLa1ruoUdgyUGylBqW/ARZ8AboxyMn/j611Wq/rUx5hfHofgL4Jvj438F/OZPft+vgf/rb/oDROS/EpF/ICL/4On6BDbQRaUd4gz9ECODshVdmlhr1rkfqL/8iPRY1xU5IjkUWDCOsr/TRcHJzkedQYWEGMfwlns2kD5QZWatcG+VDuCDxoSMQUoJZyCkmRBU/+cMKsgGnDF44zjFhXiInm/3FYzjsWZaFZ0FNWE0IYWIVHnnheZaGEVnXfNxuOi2cj9maf2dCpXSdMhjzAFeGZxOpwM8Lcd2W78/9Y0AdWzoWyvUtjKkYOyP2Tdv6ojR9A2/7w+sFU6nBX8kmZpDCxhjfA8m09zzI576cEf14+/kmJumZT44A0qTCkn9/z5OnOdFw1drI6UZ5yxfPZ24pIBr9f/m7k16bMvSNK1n9bs555jZbdw9PNrMjISsUiEmDJCYlAQj5gwYIcQQ/gNT/gnNDAaMaEpiUFWohKpEVlZlZWRGRqRHuF+/nZmds5vVM/i232QAAiqSrFDaxF1+3eX3mp2zztrf977Pc2QFDVZZVJOO+DSJ1maNiQxkLAlNVpYtdXK3pKIYncbpRs9JZvG50kujxp3RG5nO94juuzjWnx4l9lUSqcuHSKMzuAGVO6cw4rXCKuRNaJCnEo2AkpGcYo+RoBTleESvtbLH9dPfy/e5c91W1rxJDdiBMQrjjqJBlpKF1lqEgr3jw2FFMHJQ2WPOOgwD6pi/nuaJ3gTe7Jy8J6xWOGPRB2RkXVfhR7QkCL2jz96P1w4IMEWAPRVlFLkWWtXssbLHekSbJF/ba8NbJx/EeWd//khcn/n48ePRaORIhWSJfB2mBlGdHz6rXiTSGER+F2tir5HH5Qk/jQzzhO3CF3ZY0FJY0caQquRUrbXE3PFhloWXglQlF3tLhSVXllaxwygcXxuYp4ukPGolp51t2/4lj8K//PqXvZn+d8B/BPwXx1//2//TP//PlFL/FbJ4evp/nJdyvKljQemKc5o1bXK73OVFqZ0Wvav3yKpKC8gZCGNgK4lgPLV0vBupVdGVwvnxmIGKn937QNOKdVk5hxfEXEk5kmo/FgNa4MK1o4+Z5W3NaKPYooj37GC4zJ7JRPK2cfYD29ZozTCEkY/PV0zwRDRZe7ZFfkjzLCWE6+MV5wxOKcZpEkGgrbQUWfIGB1ykNY4lUqNGGcRva8R5QwieXCvT7KEdpKY9kdMBjenCSa0HcV0roYmnKuAMcdmHT7NMpTraOvIm45GcM3XfSSlxOp247ZvcnJxndIHeKy0JaSnGiHHyZrUh4LSB40mht+NmbJWofGuXBAGNppBHN2vl5hJ3HqzUHV+cLFEPmFSIKbGvhfu7mT1G9tppeLqWR79hGFhKxmnZJAcnGVGjG2mNuOAZrKO1jLdC5/fGMcwa1RKpKKZpIjeovbNXSGXjPAziE/ADjynSmiYcOedaG6lrahOaVvCaUDs1yS3Injxb3j/N4I13mM6RC/XEmmSUknZhPaiOVv1TbnX5eGWeZ3JK9C7fkxBGSpHlzDx56g6tK+YwiswwwTQOVLMSteRCXZDDNm7Sn5dqcCNu3x0emnW5yvzdeVJNQMUAKUUy8roovaEOZGBsBQtYLVbVXLMwe7NUkw2G0oRNkG6rLHd7JwuPkIaiGYVJVXryQdNzIx9QnhilVNMa6NYpKVJiYrAT+TgQ9xQxY5APyu3GeZxwiO+s93YgJUXNk0qVFM3xwduGM7E2lrQzWI9VHds7W/1rcEAppf5LZNn0Sin1FfCfI4fof6OU+k+AXwL/wfGv//dILOpnSDTqP/5/9bs4BgES61gIRxNjGmZiTjg6rkOJifu7C9d1x48DadsPmr2VQwHZ4HUtcI5UoFVD1415ngThlSLz+cJ6y7RScQN03/B1IMXKskecn7iuN7JWDNoS48Z5OktfXzda2TE9EYYOdeX+MpFUJ7bGeL5n2Z6pTcvyJwlN5zlmfJ79cQAAIABJREFUrLZMdw+E4GgpQVMoreXNYDXdaMntaXsocysamfn21rh78UAr8oK3XkDExkIvTWAXrUr4G8O2SU7TOn3EixpKmU+NmErlYJOQq2x/z3f3fPvN18SYKHknxizucuNELa0F5NKJjMMscbLgKC0ThulIKxzLhaZIVeZ79INtcLBOSyw4J/QpbRxb7Xhr2CtstXNrlqJ22dM3OM+HWK45ek2kXNDeCWG+G6x3eC0KDCG+d0rPfPbZA2mPpNbgEKu1UtjygjdevEkmsaWNokS/EsIERmF7I5bO+9sjtYjp4OK1fJRrx9oqucwigqyKqDMvTmdSTFht0VoEhcYZOiJj1KrjdEPVgkFifjkr9JG6oGQxu4aB1grbuov6pcD9/QWjA+vthtYwDYGKRlVL2nZMs+StEqynGEMYB6bThS0VpjBBcKzPV7xW1Npwbvyk7chxJymOVtFO2xOmFt6/f48bz4yXO4wdKEmWT71X9pRoReDh+3qjpMz9/Qt6q4xDQDdDd/o7BS4Yg+ryAf98e+LeB6wGrwLrvpFLEtxlrjjrKK2Q151WKtWAdpbmLDFllNZsacMOE6d5JC83UlmwQ6BU9Zf8Xa1pzQr/tHme4satNcLgMK0RVIUoi9Za/xpC+733//D/5pf+3f+Lf7cD/+n/59+FAuPBWofSE6MLJH3MoA59R2swhJPk7FAYPWKcoivBh3Vj2XOVylgWtiFVetHDMIMxaKMJweOcwqpGY2Crz3jdoGri4LBuIlfFPE18uF1RWuO1Yn26cj47hqAI+tB/1ELUidva2XPDKcu6fuB8dyGEwPL0kcEZWlfo6Y7bsvMhNiia8/AS2wvUHd89mi4zq1YJagQt7qRluzFMcsMwDalRKovRRgRlwaAOaK41lX3LYCo5FVSunGfFX3z1C168/IwPR370/iFQcuLNm2/5nZ/8mBQjpkduj++YRk9tjfF8R+tXbs83zndneq74UYLj3WpK2qTu+B1uMInNsvRO7xKfUYfLa92lVw3SBootUoosEPcIwQ0kFLGao6XT0a3TtSfuiX2XGS0BmcFphXENpwxaVUzrGGXReqK1wqs7S9oKPT2jW2FWlnkcyUpRzCEfVI0tN55Tw/oBqxTON7RObGsll8D7befk77Aq4oaJPa0Sri8N4xygSKvlaa8wTGy3hXs3c8sbYxgpLsCygUoUlYUT2kEpQ0XITaomrHHMw0jtDr0p0dQ0w/nlHTV1XKpsy84QHN4Pwt+swpbNpTAOE8Zqes1UbwkvHyh0vr09M84nei14rdGDpSuL2neWm4TYtTGchpFYdq6PG3bQlFjYe8PME00r9tvGOBlSzeiusdZz259wLlBKQI2vUX5n0xY9OpwPxF0ajYUmTa8oqYVWNaoPvL3taDo+d4LTbFuhURmnkafnZ07jxDVuODeiGlxLxBtDbIVWOxXFcr0Jys9K7LDskett4XSe0TbQmpIzRDus6/QeuPMDMWZ6Bh0sSWey6lD/hjigpJMvm3t1OF5G73BdQBBWa3kU7AXjBmrP1OORyfku29AstyKtA6WsYlU8PbAk+WTMOTM4h1aalDLTpHl+3pjmmWA7yxWCH7ntCWccW8sEIzoLOwX6WaN7lttvBT2M9KZp2hJLBTdKzo4Ted+pVjOMHn1AGWqqbM0TVcNqy1NsuF4Zg6F0mVkG69Gm0HD0Uhm7hm6lwTJ5npdnEZlpxaAH6RVXyRIabUgxiWtcG7Tt9L7xD/7+/4pVhheXM7M3WGd49/Rrbh8/cvv4ge0BTvaB29Mbrh9/xcPr36Haibdv3vL61UtBmLl2ZFA/0Js56FOa+Xwhl0iriJ9KdZnl5iLVQ33MCs2hwe4FfXh6Yq9U5bndxJkVuywdMp0lZsqepdHUkYRGL0SlybFg/EitCW1lBqmN3O4VAtJec4bacVoT3IDVGaUTFGhZnhS20mnaM0yzLPmcYnSWmjvZBG5ZTLhKdwwDy23jPI50Felo3j9HtHX4YcS0RG43xjBwaxWa5bZUtvfPXKzFOs1np3vSvslruQgeUbXvIDL9E1hb4oFNkHRI7ZneGQZPbZLSKLlhnPzMh+CgV3Jp5NqFwZASeyyEcKJVjcaQSwPrKLliUuHh4YFYMi01lmWhtfqJiN9UQ3WLd4rb7cY8SizJ+ZHWMsVoxss9z1unNXME5CMxd4zuxBrZYsY1eXRWGGYXKLmyp0jGUA/LaGmF1hTj3R3rvvG0bWhviVUWyV1rUq70WtlSJCahDHQFvYqtVzvLvm4oqximQewCXQSBWjsaAt+ehpHaG3awFCzayVgPGr3+q4tG/ZV+aaXYl5VhOuHMgPlUt5PbnsWSqsQZcm9M5xMlS8c2GM+679QEa6p4C6jGfD6Jr52MVorlunB6/Zkofu9nlPLYEIU/qjzPrZNiY9AiuBsHSwdSrzTTwVQ0nhQFWvKpxWI0uWvKtkIo6KpZlh3nR6n7GkUqcFtWNhNwfqAZ8RRNIWBI1K7IW6E1hyaSY+Z+FueTWBp31nXFOFlMfLflH0/zEchPWBdEH60qOSZi2QWDVwvzyfGLX/zv/Nkvv+Kzz36Pn/zelzyMFfO88Ms/+nt8/etv8WGkUjm9f+L3/tbfQucn3n31BuMs0+WO2/PC68++4HHZOZ1OpC3hrea6bDy8+Ix1fSbXhDKebjz7YSRVVmhGBYOKmdqi3K5QXJcdZWeeUhFdd6pkpejOov1ALgWturidXEcR0EbC1tZarNVQxSfftWaylpY21pgYjZfbujVkU3hehDE7zhN7TugwopohpoJRBtcreV8Y3R03IKIYxpFaCgf/BaUr0zjw9Lyh3cj1+sRpELDxNJ24bRub1YxuFoUHldtecKVhKMxGDBKlNTydskXJcbaO9+EAJo+fFlF5FzKUG4RLO5xmVG0oDLVl1hTxqkDrlCPKNtsTwY/4MBFToilPVeBsJ8eItYYWPLUVvDEUDSVlmX8XRbeOrVWc9aS8YrzjeXvGKS1Pbcqz35qYQFs7ICUNZ0/Uktm3ndZvWB/QOpBaJ/gzT6Wx7BE/zpIlb4VysHe31tlSwvuRwQV6y/DdCA9LU5bH20Y3FWUCHRiGido2VGtUlAB84kpOUmUehxOxLAR/wVmB36Qs1o5gO9p7YpOnrZQSw930G59jvxWHae8dbwdU09TSsUbLi2hyqFpAa6w3KG3xKEpMoD3OBvacKd3K9toE3BS43d7RNgFHzEGiJvMowORxmkmpEJNFO3nc8gqm00RLmevaGIKibomqRpS18kOv0hyaToNYG72nVsW2R87nE15VglWSBzUjW1P4cebjFjHDIFizWillw4eJSiHuO1UVnBbXUd4jWlVG6/Gq0frGFhe08mAMwzRKAaBmfPCkXHHOUxtcr1cGB3/2s3/Bq1cvyOmK8eJYevfuzync8OeA4gPP32S2+A61LSzLrxjvXqDDSz7/3u/w9OFX/L3/+b/GmxM/+v4PaHj++I//OT/+4Y+hPmB8ZbCwpQUznzGqs+ybMFO1zEFT15K5bZpcO7c90Y3nMUGtFlVANU3solcOzXDbI0pprA3seyIEudGlPeKaEZVF0BQqRTVKaVgzYY0jZTlQ123HGoW2AwVDaoWtNOIGJQWs1Sy3TEWjt11AGNowe0doG8oFASJ3Q88ZFxTeGbxpeGexdSc0x2w727ZxPs20Uim18nxVjMOF53XhsS7QFJP3mN7EimADqlWUsmgS67oyhoF133E6SC72IJellDBWeAm9FTEnGCe3spSPDb4I86oW+HNrCRsGPqb9eHrqFK1RNrBsK/d2lEp06aghcKcCtlRu5Qra0d3M2gy3tdC7JbRGbVayrmFGuYmnmJm1xlnDcr1RUThraXWn213iVlZj+kBthlsEpT15h0wHO9IbDCoT7AEXUgYMWOepMdOqfK+dG0F1cirECs548jHi0hrytuK8whhP1Y31+YYGrAlor7HKUq0jFRGPpiJcZK01aZUUg3EOunBd19v+G59jvxWHKUiTxWkI48iWIspaQtModYB8labXxmAtK4INU9phvBUFRu3suVJ1Zz69kAB6hQ9L5u5BaPjaO1LKjMGTWqZqhTUz3Qy8eb+ydY1VF3LMjKeTHOSt4JwlLZH7izAT0ZZ9L5Ta0FqxLk+8OM+sa0P5TusN7wdyLdQ9M51GTCnUIjk3W6Tf3HrDuYBSlnzUZ73TWNPodUGryv3LlzxdI6JnPvTP3otWxEqYvjXwwbLvG3uK/LM/+kd88fqey93Eq5eGON3xJz//Fb/z6nep7R1/8ss/QvuMipmvP/yMH375b/C9l1/wJ//8f+HuxQzukSX9mj/8sz/m889+yrpeUL2zLFey7Tw9Zl7cvyCmRbBztZBaZz6eKCxZPFzGEJxlXTa681xmS2qydHrOCaqh9sLHrdPVQO2V9emR8zRitGDd8J64F6w5scSEHrzErqxnXSLQCYOhZnG3dydMh/2ojtYCzsyESWRzrcJkLCZI972XhaAtd65SN0U3HaULk1VYK8bUkkH1zL2HGp85uwGlNY97RwUpZ+z7znJbJOu5LGhr0XReDYVz6JAjtYnN9rv4W+9dvEfascWEN4e+21pyiVANSoMLM+u6stzkAHbWA4lUd0HhNVDhTJtOxFLZlojxMx+uK65C7yPPTxVnZuiVuiWyhdkG8mzJHd7cIPeBhGzthxoxWIzxjOMZ7QeUrnzcG1MYKS3QSzlys5olG/RwR+6dngodI+oQpenaYIyn60ytkdhWSTEkUKrQtaFpWVLWhnxo9H7YCgzd9sMg6uWS5TtqVOQ9Yw4pYysd6y3rwR9wYZTZthFjQG+KqArKQG0G04GaGbGkPZN+82X+bwccWmtNCI7aMtu20rrAXeO+0VphX2+Hu0ULh7LD+Xz+JJG73nYqivl8xxITDU3tBowlYSjKsKbM0/X5k7O7E1Fa83Rb+MWbhaUOrH3kthVSMayrHKTeOlRrnKeRbV+OcLpkJ7UW2HSvmWVZCOOF1JBtfS302rhMAmKhRIbgGKzBKk2Nu2T0OuTa6QqGMDLPs0SVTOc8n1jXnXLQxHPOh2cKtLKii0jpYDdKlu96u/H4/g1/+E/+Nz68/YZt+cC7t19TU+bNr77h6zd/jjkp3l/fEttG97DsH/n27Z/w6kWlxEdq20gl8/R8Y5gunE4XPrx/5Otf/ZpaKz5YvLdcLiestdzWhWGcKUUe++YQmILF6o5umYf7E85o5po5K7BxY/Ka8+CYsQzzCetHcAY/zbghUFuR9gtSd1xTQR/YOAFXF6zxaGtEAtgg5UqsjW3b6GjWFKlF0RqUGlFV8IWjVShVuS3P3F1GptFieuXl+cRgDZfB82r2nJ3iHBz7spL2yOAclIylYXpG9Yo2Qo7y44CympYzp9MJqzXWKM6DZTKZyWpO0ywZ0SrqGqUkJbBF+RkDn0hd47Hsc85xuy1Y7xmHGefCIUzUh++oiENpOJPRrK2TVOBxyTwnRVYDW9ZkNfC8VW4RarfcYufdkvi4ZT7ulT5dqHZk7469GlJ32HDBuIll77x5d+V6izyriZ+9vfE+WtYC1VhusbLlkVsaaPYVzY9U7UmtsUap9sqfVdI2tbeD1CQHt0ERnNzSjRPfU+pdssT9cK/RUcZQWqbWwrreUEo+4OX8EP2IHTxKW3IXzuwWE6U1nLVohAhWUJ8KL4+Pz2zbxnL7V5cz/Sv9al1aIBpD6RVvhLKjjAWt8MNMPLJjjzEyznc8Pl3JqpOuokfeYsWWhG6d26KoWGEuqhd8eNsp1RFCR/XEh3fvsLkxDIbUNdo2HkbLnjOty/Kn5caSO9EVqAU7aXQT82fTkv1TzjN4S9Yya6XsvApeanPGooO0P3Ku3B9LEzEzKky3pLKTSyQ3ix8GrHpmyArdpHe+bjessgzGs143jHMM1rHnjZISrlmonaLF7eOnwDxbvq5PeHflF3/6D7GD4i/e/RI3DXy9/VySEFzwVNns6oHaVt6+/zku/C5//oufo5vBMnN3umMIZ37y4++xL5aPHxZmPXO++wxjRkrdMc7y+eefM1hPzgWn9SevkD3qpaY2vCo010EXkiqgGxXN7jp2e6Z0gx0dpTSc7igFLjSiqdQamV8Y9pIJoyHWSjGGrhJGyZ9dn45WkDXsdWAeLOd+ohlJGnRtaUYTe6dqLbng0bA87aBlIfS2bNQueuySqxCLVGeePGvOvH1eCNqLItg6zjYzuJ2mZBY3n0e2VFnWnaf9xsPLE6ZllNK0rniO8diAG/YCtECwhnHuqF5QwGmc2ZaVvVT8PJJ7xfhOa1nKAYCxloZly5U2vmZTltwbtXnW5riWgjKKYexs2y4H2aHlaF1z2zOrlZB+ygZnDD0Wqeb2BtZTcXxIiZIm0IpMQXXDfkvASEwKo8+YKq/l2SVsz+T0iGoJrxVUeUrwcTlaXVYUPVuk9IzTnSmM5IK8nrWjZhnPKSuJGXfYRXXPtL3QjWMrjdANOTUU0tXvdMwwUjKEccJoS1qumFHU6c17itY41ajbVfY0MWIHR0yVYfwbQtrvvbOlSBgmctXEmI95iihg1y2ScyYMRoLC1/fSpvGeLWes8ZxGzdu3j4zzhQ9Hv3yvO6MvGN0Jk8zWhuFELYp5rKhemYOnlEyNz7yczqy90lRHW8PtUCOkU

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/150169.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 【SpringBoot】15、SpringBoot中使用Kaptcha实现验证码

    【SpringBoot】15、SpringBoot中使用Kaptcha实现验证码当我们在项目中登录使用验证码的时候,不妨试试Kaptcha生成验证码,非常简单1、我们在pom.xml文件中引入kaptcha的maven依赖<!–kaptcha验证码–><dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha&lt…

  • linux下解压命令大全「建议收藏」

    linux下解压命令大全「建议收藏」.tar解包:tarxvfFileName.tar打包:tarcvfFileName.tarDirName(注:tar是打包,不是压缩!)———————————————.gz解压1:gunzipFileName.gz解压2:gzip-dFileName.gz压缩:gzipFileName.tar.gz和.tgz解压:tarzxvfF…

  • JAVA string转map_java怎么转业务

    JAVA string转map_java怎么转业务String转Mapstring转map的时候,很多新人可能不会去判断string的内容是什么格式的,因为map是key-value格式的,但是string就是一个字符串,想想,这个应该不能转吧,我就遇到过,想想就觉得自己傻傻的,哈哈哈。看代码 Stringcontent=””;HashMap<String,Object>map=newHashMap<>();try{map=JS

  • centos系统不识别u盘_Unable to locate package vim

    centos系统不识别u盘_Unable to locate package vim在修改源后使用sudoapt-getupdate 显示 W:GPGerror:https://mirrors.ustc.edu.cn/kalikali-rollingInRelease:Thefollowingsignaturescouldn’tbeverifiedbecausethepublickeyisnotavailable:…

    2022年10月13日
  • jsp:include引入jsp文件的时候(如果被包含的文件存在js文件),不要用相对路径

    jsp:include引入jsp文件的时候(如果被包含的文件存在js文件),不要用相对路径先回忆一下二者的区别(对于此篇文章而言,二者在用法上没有区别) jsp:include是先编译一下included.jsp文件,然后再包含 先编译,后包含@ include是先把文件包含就来,然后统一编译 先包含,后编译  今天下午想把jQuery集成到项目中,发现怎么样都会出问题。原因就是路径问题。在集成时,我的思路是…

  • idea如何集成svn_集成吊顶步骤分解图

    idea如何集成svn_集成吊顶步骤分解图idea从项目窗口跳到打开项目选项窗口操作之后即可跳到如下界面第一步:下载svn的客户端,通俗一点来说就是小乌龟啦!官网下载地址:Downloads·TortoiseSVN下载之后直接安装就好了,但是要注意这里,选择安装所有的命令行客户端工具,默认是不安装的,如果不安装,svn中的bin目录下就会没有svn.exe,这个待会会用到,所以一点要注意哦。(都是坑啊)然后就下一步下一步就安装好了。第二步:如果已经搭建好了svn服务的话,就要开始在idea中配置相关.

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号