分类目录归档:深度学习

AutoGrad 这个Python神器能够帮你自动计算函数斜率和梯度

AutoGrad

AutoGrad 是一个老少皆宜的 Python 梯度计算模块。

对于初高中生而言,它可以用来轻易计算一条曲线在任意一个点上的斜率。

对于大学生、机器学习爱好者而言,你只需要传递给它Numpy这样的标准数据库下编写的损失函数,它就可以自动计算损失函数的导数(梯度)。

我们将从普通斜率计算开始,介绍到如何只使用它来实现一个逻辑回归模型。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install autograd

2.AutoGrad 计算斜率

对于初高中生同学而言,它可以用来轻松计算斜率,比如我编写一个斜率为0.5的直线函数:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad


def oneline(x):
    y = x/2
    return y

grad_oneline = grad(oneline)
print(grad_oneline(3.0))

运行代码,传入任意X值,你就能得到在该X值下的斜率:

(base) G:\push\20220724>python 1.py
0.5

由于这是一条直线,因此无论你传什么值,都只会得到0.5的结果。

那么让我们再试试一个tanh函数:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad

def tanh(x):
    y = np.exp(-2.0 * x)
    return (1.0 - y) / (1.0 + y)
grad_tanh = grad(tanh)
print(grad_tanh(1.0))

此时你会获得 1.0 这个 x 在tanh上的曲线的斜率:

(base) G:\push\20220724>python 1.py
0.419974341614026

我们还可以绘制出tanh的斜率的变化的曲线:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad


def tanh(x):
    y = np.exp(-2.0 * x)
    return (1.0 - y) / (1.0 + y)
grad_tanh = grad(tanh)
print(grad_tanh(1.0))

import matplotlib.pyplot as plt
from autograd import elementwise_grad as egrad
x = np.linspace(-7, 7, 200)
plt.plot(x, tanh(x), x, egrad(tanh)(x))
plt.show()

图中蓝色的线是tanh,橙色的线是tanh的斜率,你可以非常清晰明了地看到tanh的斜率的变化。非常便于学习和理解斜率概念。

3.实现一个逻辑回归模型

有了Autograd,我们甚至不需要借用scikit-learn就能实现一个回归模型:

逻辑回归的底层分类就是基于一个sigmoid函数:

import autograd.numpy as np
from autograd import grad

# Build a toy dataset.
inputs = np.array([[0.52, 1.12,  0.77],
                   [0.88, -1.08, 0.15],
                   [0.52, 0.06, -1.30],
                   [0.74, -2.49, 1.39]])
targets = np.array([True, True, False, True])

def sigmoid(x):
    return 0.5 * (np.tanh(x / 2.) + 1)

def logistic_predictions(weights, inputs):
    # Outputs probability of a label being true according to logistic model.
    return sigmoid(np.dot(inputs, weights))

从下面的损失函数可以看到,预测结果的好坏取决于weights的好坏,因此我们的问题转化为怎么优化这个 weights 变量:

def training_loss(weights):
    # Training loss is the negative log-likelihood of the training labels.
    preds = logistic_predictions(weights, inputs)
    label_probabilities = preds * targets + (1 - preds) * (1 - targets)
    return -np.sum(np.log(label_probabilities))

知道了优化目标后,又有Autograd这个工具,我们的问题便迎刃而解了,我们只需要让weights往损失函数不断下降的方向移动即可:

# Define a function that returns gradients of training loss using Autograd.
training_gradient_fun = grad(training_loss)

# Optimize weights using gradient descent.
weights = np.array([0.0, 0.0, 0.0])
print("Initial loss:", training_loss(weights))
for i in range(100):
    weights -= training_gradient_fun(weights) * 0.01

print("Trained loss:", training_loss(weights))

运行结果如下:

(base) G:\push\20220724>python regress.py
Initial loss: 2.772588722239781
Trained loss: 1.067270675787016

由此可见损失函数以及下降方式的重要性,损失函数不正确,你可能无法优化模型。损失下降幅度太单一或者太快,你可能会错过损失的最低点。

总而言之,AutoGrad是一个你用来优化模型的一个好工具,它可以给你提供更加直观的损失走势,进而让你有更多优化想象力。有兴趣的朋友还可以看官方的更多示例代码:

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 量化投资的强化学习神器!FinRL 入门指南

关于强化学习的基础知识,可以阅读我们以前发表的一篇基础文章:

什么是强化学习?预测股票的效果如何?

使用强化学习预测股价,类似于心理学中的操作性条件反射原理,你需要在决策的时候采取合适的行动 (Action) 使奖励最大化。与监督学习预测未来的数值不同,强化学习根据输入的状态(如当日开盘价、收盘价等),输出系列动作(例如:买进、持有、卖出),并对好的动作结果不断进行奖励,对差的动作结果不断进行惩罚,使得最后的收益最大化,实现自动交易。

如果你从头开始编写一套强化学习的代码,时间成本和试错成本会比较高。而本文的主角 FinRL 框架,能够帮助你极大地减少学习成本、时间成本和试错成本。下面就介绍一下 FinRL 的使用方法。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

# 首先克隆项目
git clone https://github.com/AI4Finance-Foundation/FinRL.git
# 进入刚克隆的项目,安装依赖
cd FinRL
pip install 

请注意 Python 版本要大于等于 3.7。此外,如果你的当前Python环境下安装了 zipline,请 pip uninstall 掉 zipline,因为Zipline与FinRL不兼容。

可能出现的错误:

如果你出现以下红字提示:

error: command 'swig.exe' failed: No such file or directory

请使用conda安装swig:

conda install swig

然后重新执行 pip install . 即可。

2.模型训练

运行官方示例的时候会使用到雅虎财经的数据,雅虎财经在中国已经关闭服务,因此你会需要VPN才能下载雅虎财经的数据。

cd FinRL
python Stock_NeurIPS2018.py

运行的时候大概率会遇到这个问题(2022-07-03):

FileNotFoundError: Please set your own ALPACA_API_KEY and ALPACA_API_SECRET in config_private.py

这是官网的一个不严谨实现导致的,你可以将 finrl/main.py 中25~30行的代码移动到第100行,如下所示:

此外,在运行代码的时候,你可能会遇到无法下载数据的问题,这是因为雅虎财经在中国已经关闭服务,你需要在 Stock_NeurIPS2018.py 的第172行代码 fetch_data 函数中添加proxy参数:

# 公众号:二七阿尔量化
# 此处我的代理是10809端口,你应该按需修改
df = YahooDownloader(start_date = '2009-01-01',
                     end_date = '2021-10-31',
                     ticker_list = config_tickers.DOW_30_TICKER
                     ).fetch_data(proxy={"http": "http://127.0.0.1:10809", "https": "https://127.0.0.1:10809"})

此外,在 finrl/finrl_meta/preprocessor/preprocessors.py 的第191行,你也需要增加proxy参数:

# 公众号:二七阿尔量化
# 此处我的代理是10809端口,你应该按需修改
df_vix = YahooDownloader(
            start_date=df.date.min(), end_date=df.date.max(), ticker_list=["^VIX"]
        ).fetch_data(proxy={"http": "http://127.0.0.1:10809", "https": "https://127.0.0.1:10809"})

正常运行起来的模型训练如下图所示:

下面是我简化版的到SAC模型训练为止的全部代码:

# 公众号:二七阿尔量化
from finrl import config
from finrl import config_tickers
from finrl.main import check_and_make_directories

import pandas as pd

from finrl.finrl_meta.preprocessor.yahoodownloader import YahooDownloader
from finrl.finrl_meta.preprocessor.preprocessors import FeatureEngineer, data_split
from finrl.finrl_meta.env_stock_trading.env_stocktrading import StockTradingEnv
from finrl.agents.stablebaselines3.models import DRLAgent

import sys
sys.path.append("../FinRL-Library")

import itertools

from finrl.config import (
    DATA_SAVE_DIR,
    TRAINED_MODEL_DIR,
    TENSORBOARD_LOG_DIR,
    RESULTS_DIR,
)

check_and_make_directories([DATA_SAVE_DIR, TRAINED_MODEL_DIR, TENSORBOARD_LOG_DIR, RESULTS_DIR])


'''
# Part 1. 下载数据
'''

df = YahooDownloader(
    start_date='2009-01-01',
    end_date='2021-10-31',
    ticker_list=config_tickers.DOW_30_TICKER
).fetch_data(proxy={"http": "http://127.0.0.1:10809", "https": "https://127.0.0.1:10809"})


print(f"config_tickers.DOW_30_TICKER: {config_tickers.DOW_30_TICKER}")


print(f"df.shape: {df.shape}")


df.sort_values(['date','tic'],ignore_index=True).head()


'''
# Part 2: 数据预处理
'''

fe = FeatureEngineer(
                    use_technical_indicator=True,
                    tech_indicator_list=config.INDICATORS,
                    use_vix=True,
                    use_turbulence=True,
                    user_defined_feature = False)

processed = fe.preprocess_data(df)


list_ticker = processed["tic"].unique().tolist()
list_date = list(pd.date_range(processed['date'].min(),processed['date'].max()).astype(str))
combination = list(itertools.product(list_date,list_ticker))

processed_full = pd.DataFrame(combination,columns=["date","tic"]).merge(processed,on=["date","tic"],how="left")
processed_full = processed_full[processed_full['date'].isin(processed['date'])]
processed_full = processed_full.sort_values(['date','tic'])

processed_full = processed_full.fillna(0)


processed_full.sort_values(['date','tic'],ignore_index=True).head(10)

# 训练集
train = data_split(processed_full, '2009-01-01','2020-07-01')
# 测试集
trade = data_split(processed_full, '2020-07-01','2021-10-31')

print(f"len(train): {len(train)}")
print(f"len(trade): {len(trade)}")
print(f"train.tail(): {train.tail()}")
print(f"trade.head(): {trade.head()}")
print(f"config.INDICATORS: {config.INDICATORS}")
stock_dimension = len(train.tic.unique())
state_space = 1 + 2*stock_dimension + len(config.INDICATORS)*stock_dimension
print(f"Stock Dimension: {stock_dimension}, State Space: {state_space}")

buy_cost_list = sell_cost_list = [0.001] * stock_dimension
num_stock_shares = [0] * stock_dimension

env_kwargs = {
    "hmax": 100,
    "initial_amount": 1000000,
    "num_stock_shares": num_stock_shares,
    "buy_cost_pct": buy_cost_list,
    "sell_cost_pct": sell_cost_list,
    "state_space": state_space,
    "stock_dim": stock_dimension,
    "tech_indicator_list": config.INDICATORS,
    "action_space": stock_dimension,
    "reward_scaling": 1e-4
}


e_train_gym = StockTradingEnv(df = train, **env_kwargs)

env_train, _ = e_train_gym.get_sb_env()
print(f"type(env_train): {type(env_train)}")


'''
# Part 3: 模型训练
'''
agent = DRLAgent(env = env_train)
SAC_PARAMS = {
    "batch_size": 128,
    "buffer_size": 1000000,
    "learning_rate": 0.0001,
    "learning_starts": 100,
    "ent_coef": "auto_0.1",
}

model_sac = agent.get_model("sac", model_kwargs = SAC_PARAMS)


trained_sac = agent.train_model(model=model_sac,
                             tb_log_name='sac',
                             total_timesteps=60000)

3.模型测试

在这一部分,我们将使用测试集进行模拟交易,检验模型的效果。

在env_kwargs中,我们设置了初始资金为1000000美元,测试也会以这个初始资金为起点。

# 测试
e_trade_gym = StockTradingEnv(df=trade, turbulence_threshold=70, risk_indicator_col='vix', **env_kwargs)
df_account_value, df_actions = DRLAgent.DRL_prediction(
    model=trained_sac,
    environment=e_trade_gym
)
print(f"df_account_value.tail(): {df_account_value.tail()}")

如下:

此外,df_actions内保存了每天的持仓记录:

print(f"df_actions.head(): {df_actions.head()}")

调用 backtest_stats 函数,能获得完整的回测结果:

print("==============Get Backtest Results===========")
now = datetime.datetime.now().strftime('%Y%m%d-%Hh%M')

perf_stats_all = backtest_stats(account_value=df_account_value)
perf_stats_all = pd.DataFrame(perf_stats_all)
perf_stats_all.to_csv("./"+config.RESULTS_DIR+"/perf_stats_all_"+now+'.csv')

结果如下所示:

可以见到,模型的年化收益为30%,累计净值收益为43%.

但是这段时间为美股的牛市,我们还需要以道琼斯指数为基准计算超额收益,才能更直观地展示模型的效果:

print("==============Get Baseline Stats===========")
baseline_df = get_baseline(
        ticker="^DJI",
        start = df_account_value.loc[0,'date'],
        end = df_account_value.loc[len(df_account_value)-1,'date'])

stats = backtest_stats(baseline_df, value_col_name = 'close')

可见模型还是具有超额收益的,我们将其绘制为图表更清晰地表达:

backtest_result = backtest_plot(df_account_value, 
             baseline_ticker = '^DJI', 
             baseline_start = df_account_value.loc[0,'date'],
             baseline_end = df_account_value.loc[len(df_account_value)-1,'date'])
with open("backtest_result.html", "w") as file:
    file.write(backtest_result)

作者给我们内置了许多漂亮的回测图表,非常好用。但我们只需要看最关键的cumulative returns. 从图中可以看到这个模型(绿色的线条)一开始的表现并不如指数,但是到了后面,它的表现渐渐优于指数。

当然,这是官方给的示例数据,大家可以用自己的因子补充数据,将模型完善地更好。本文的示例中使用的是SAC模型,你也可以尝试其他的强化学习模型。

总之,Finrl 只能提供你一双”巨人的肩膀“,你应该根据自己的实际业务场景和数据类型使用不同的优化方法。

4.其他

FinRL不只能支持美股,它还支持A股的部分数据源,如聚宽、米筐和Tushare:

以downloader为例,用法很简单,库中提供了 Tushare 的 downloader, 你只需要把:

from finrl.finrl_meta.preprocessor.yahoodownloader import YahooDownloader

替换为:

from finrl.finrl_meta.preprocessor.tusharedownloader import TushareDownloader

并进行相应的代码修改即可,当然,除此之外还有许多细节问题需要处理,由于文章篇幅的问题,我们留到下篇文章再给大家介绍。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 大规模深度学习分布式框架 DeepSpeed 使用指南

最常见的深度学习框架应该是TensorFlow、Pytorch、Keras,但是这些框架在面向大规模模型的时候都不是很方便。

比如Pytorch的分布式并行计算框架(Distributed Data Parallel,简称DDP),它也仅仅是能将数据并行,放到各个GPU的模型上进行训练。

也就是说,DDP的应用场景在你的模型大小大于显卡显存大小时,它就很难继续使用了,除非你自己再将模型参数拆散分散到各个GPU上。

今天要给大家介绍的DeepSpeed,它就能实现这个拆散功能,它通过将模型参数拆散分布到各个GPU上,以实现大型模型的计算,弥补了DDP的缺点,非常方便,这也就意味着我们能用更少的GPU训练更大的模型,而且不受限于显存。

DeepSpeed入门并不简单,尽管是微软开源的框架,文档却写的一般,缺少条理性,也没有从零到一的使用示例。下面我就简单介绍一下怎么使用DeepSpeed这个框架。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install deepspeed

此外,你还需要下载 Pytorch,在官网选择自己对应的系统版本和环境,按照指示安装即可:

https://pytorch.org/get-started/locally/

2.使用 DeepSpeed 分布式框架

使用DeepSpeed其实和写一个pytorch模型只有部分区别,一开始的流程是一样的。

2.1 载入数据集:

import torch
import torchvision
import torchvision.transforms as transforms

trainset = torchvision.datasets.CIFAR10(root='./data',
                                        train=True,
                                        download=True,
                                        transform=transform)
trainloader = torch.utils.data.DataLoader(trainset,
                                          batch_size=16,
                                          shuffle=True,
                                          num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data',
                                       train=False,
                                       download=True,
                                       transform=transform)
testloader = torch.utils.data.DataLoader(testset,
                                         batch_size=4,
                                         shuffle=False,
                                         num_workers=2)

2.2 编写模型:

import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()
criterion = nn.CrossEntropyLoss()

这里我写了一个非常简单的模型作测试。

2.3 初始化Deepspeed

DeepSpeed 通过输入参数来启动训练,因此需要使用argparse解析参数:

import argparse


def add_argument():
    parser = argparse.ArgumentParser(description='CIFAR')
    parser.add_argument('-b',
                        '--batch_size',
                        default=32,
                        type=int,
                        help='mini-batch size (default: 32)')
    parser.add_argument('-e',
                        '--epochs',
                        default=30,
                        type=int,
                        help='number of total epochs (default: 30)')
    parser.add_argument('--local_rank',
                        type=int,
                        default=-1,
                        help='local rank passed from distributed launcher')

    parser.add_argument('--log-interval',
                        type=int,
                        default=2000,
                        help="output logging information at a given interval")

    parser = deepspeed.add_config_arguments(parser)
    args = parser.parse_args()
    return args

此外,模型初始化的时候除了参数,还需要model及其parameters,还有训练集:

args = add_argument()
net = Net()
parameters = filter(lambda p: p.requires_grad, net.parameters())
model_engine, optimizer, trainloader, __ = deepspeed.initialize(
    args=args, model=net, model_parameters=parameters, training_data=trainset)

2.4 训练逻辑

下面的部分和我们平时训练模型是几乎一样的代码,请注意 local_rank 是你不需要管的参数,在后面启动模型训练的时候,DeepSpeed会自动给这个参数赋值。

for epoch in range(2):
    running_loss = 0.0
    for i, data in enumerate(trainloader):
        inputs, labels = data[0].to(model_engine.local_rank), data[1].to(
            model_engine.local_rank)
        outputs = model_engine(inputs)
        loss = criterion(outputs, labels)
        model_engine.backward(loss)
        model_engine.step()

        # print statistics
        running_loss += loss.item()
        if i % args.log_interval == (args.log_interval - 1):
            print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / args.log_interval))
            running_loss = 0.0

2.5 测试逻辑

模型测试和模型训练的逻辑类似:

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images.to(model_engine.local_rank))
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels.to(
            model_engine.local_rank)).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' %
      (100 * correct / total))

2.6 编写模型参数

在当前目录下新建一个 config.json 里面写好我们的调优器、训练batch等参数:

 {
   "train_batch_size": 4,
   "steps_per_print": 2000,
   "optimizer": {
     "type": "Adam",
     "params": {
       "lr": 0.001,
       "betas": [
         0.8,
         0.999
       ],
       "eps": 1e-8,
       "weight_decay": 3e-7
     }
   },
   "scheduler": {
     "type": "WarmupLR",
     "params": {
       "warmup_min_lr": 0,
       "warmup_max_lr": 0.001,
       "warmup_num_steps": 1000
     }
   },
   "wall_clock_breakdown": false
 }

完整的开发流程就结束了,可以看到其实和我们平时使用pytorch开发模型的区别不大,就是在初始化的时候使用 DeepSpeed,并以输入参数的形式初始化。完整代码可以在Python实用宝典后台回复 Deepspeed 下载。

3. 测试代码

现在就来测试我们上面的代码能不能正常运行。

在这里,我们需要用环境变量控制使用的GPU,比如我的机器有10张GPU,我只使用6, 7, 8, 9号GPU,输入命令:

export CUDA_VISIBLE_DEVICES="6,7,8,9"

然后开始运行代码:

deepspeed test.py --deepspeed_config config.json

看到下面的输出说明开始正常运行,在下载数据了:

开始训练的时候 DeepSpeed 通常会打印更多的训练细节供用户监控,包括训练设置、性能统计和损失趋势,效果类似于:

worker-0: [INFO 2020-02-06 20:35:23] 0/24550, SamplesPerSec=1284.4954513975558
worker-0: [INFO 2020-02-06 20:35:23] 0/24600, SamplesPerSec=1284.384033658866
worker-0: [INFO 2020-02-06 20:35:23] 0/24650, SamplesPerSec=1284.4433482972925
worker-0: [INFO 2020-02-06 20:35:23] 0/24700, SamplesPerSec=1284.4664449792422
worker-0: [INFO 2020-02-06 20:35:23] 0/24750, SamplesPerSec=1284.4950124403447
worker-0: [INFO 2020-02-06 20:35:23] 0/24800, SamplesPerSec=1284.4756105952233
worker-0: [INFO 2020-02-06 20:35:24] 0/24850, SamplesPerSec=1284.5251526215386
worker-0: [INFO 2020-02-06 20:35:24] 0/24900, SamplesPerSec=1284.531217073863
worker-0: [INFO 2020-02-06 20:35:24] 0/24950, SamplesPerSec=1284.5125323220368
worker-0: [INFO 2020-02-06 20:35:24] 0/25000, SamplesPerSec=1284.5698818883018
worker-0: Finished Training
worker-0: GroundTruth:    cat  ship  ship plane
worker-0: Predicted:    cat   car   car plane
worker-0: Accuracy of the network on the 10000 test images: 57 %

当你运行到最后,出现了这样的输出,恭喜你,完成了你的第一个 DeepSpeed 模型,可以开始你的大规模训练之路了。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

NNI 一个Python帮你自动做机器学习调参的神器

NNI 自动机器学习调参,是微软开源的又一个神器,它能帮助你找到最好的神经网络架构或超参数,支持各种训练环境

它常用的使用场景如下:

  • 想要在自己的代码、模型中试验不同的机器学习算法
  • 想要在不同的环境中加速运行机器学习。
  • 想要更容易实现或试验新的机器学习算法的研究员或数据科学家,包括:超参调优算法,神经网络搜索算法以及模型压缩算法。

它支持的框架有:

  • PyTorch
  • Keras
  • TensorFlow
  • MXNet
  • Caffe2
  • Scikit-learn
  • XGBoost
  • LightGBM

基本上市面上所有的深度学习和机器学习的框架它都支持。

下面就来看看怎么使用这个工具。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install --upgrade nni

2.NNI 机器学习调参运行示例

让我们运行一个示例来验证是否安装成功,首先克隆项目:

git clone -b v2.6 https://github.com/Microsoft/nni.git

(如果你无法成功克隆项目,请在Python实用宝典后台回复nni下载项目)

运行 MNIST-PYTORCH 示例,Linux/macOS:

nnictl create --config nni/examples/trials/mnist-pytorch/config.yml

Windows:

nnictl create --config nni\examples\trials\mnist-pytorch\config_windows.yml

出现这样的界面就说明安装成功,示例运行正常:

访问 http://127.0.0.1:8080 可以配置运行时间、实验次数等:

3.模型自动调参配置

那么如何让它和我们自己的模型适配呢?

观察 config_windows.yaml 会发现:

searchSpaceFile: search_space.json
trialCommand: python mnist.py
trialGpuNumber: 0
trialConcurrency: 1
tuner:
  name: TPE
  classArgs:
    optimize_mode: maximize
trainingService:
  platform: local

我们先看看 trialCommand, 这很明显是训练使用的命令,训练代码位于 mnist.py,其中有部分代码如下:

def get_params():
    # Training settings
    parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
    parser.add_argument("--data_dir", type=str,
                        default='./data', help="data directory")
    parser.add_argument('--batch_size', type=int, default=64, metavar='N',
                        help='input batch size for training (default: 64)')
    parser.add_argument("--batch_num", type=int, default=None)
    parser.add_argument("--hidden_size", type=int, default=512, metavar='N',
                        help='hidden layer size (default: 512)')
    parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
                        help='learning rate (default: 0.01)')
    parser.add_argument('--momentum', type=float, default=0.5, metavar='M',
                        help='SGD momentum (default: 0.5)')
    parser.add_argument('--epochs', type=int, default=10, metavar='N',
                        help='number of epochs to train (default: 10)')
    parser.add_argument('--seed', type=int, default=1, metavar='S',
                        help='random seed (default: 1)')
    parser.add_argument('--no_cuda', action='store_true', default=False,
                        help='disables CUDA training')
    parser.add_argument('--log_interval', type=int, default=1000, metavar='N',
                        help='how many batches to wait before logging training status')


    args, _ = parser.parse_known_args()
    return args

如上所示,这个模型里提供了 10 个参数选择。也就是说 NNI 可以帮我们自动测试这10个参数。

那么这些参数在哪里设定?答案是在 searchSpaceFile 中,对应的值也就是 search_space.json:

{
    "batch_size": {"_type":"choice", "_value": [16, 32, 64, 128]},
    "hidden_size":{"_type":"choice","_value":[128, 256, 512, 1024]},
    "lr":{"_type":"choice","_value":[0.0001, 0.001, 0.01, 0.1]},
    "momentum":{"_type":"uniform","_value":[0, 1]}
}

这里有4个选项,NNI 是怎么组合这些参数的呢?这就是tuner参数干的事,为了让机器学习和深度学习模型适应不同的任务和问题,我们需要进行超参数调优,而自动化调优依赖于优秀的调优算法。NNI 内置了先进的调优算法,并且提供了易于使用的 API。

在 NNI 中,调优算法被称为“tuner”。Tuner 向 trial 发送超参数,接收运行结果从而评估这组超参的性能,然后将下一组超参发送给新的 trial。

下表简要介绍了 NNI 内置的调优算法。

Tuner算法简介
TPETree-structured Parzen Estimator (TPE) 是一种基于序列模型的优化方法 (sequential model-based optimization, SMBO)。SMBO方法根据历史数据来顺序地构造模型,从而预估超参性能,并基于此模型来选择新的超参。参考论文
Random Search (随机搜索)随机搜索在超算优化中表现出了令人意外的性能。如果没有对超参分布的先验知识,我们推荐使用随机搜索作为基线方法。参考论文
Anneal (退火)朴素退火算法首先基于先验进行采样,然后逐渐逼近实际性能较好的采样点。该算法是随即搜索的变体,利用了反应曲面的平滑性。该实现中退火率不是自适应的。
Naive Evolution(朴素进化)朴素进化算法来自于 Large-Scale Evolution of Image Classifiers。它基于搜索空间随机生成一个种群,在每一代中选择较好的结果,并对其下一代进行变异。朴素进化算法需要很多 Trial 才能取得最优效果,但它也非常简单,易于扩展。参考论文
SMACSMAC 是基于序列模型的优化方法 (SMBO)。它利用使用过的最突出的模型(高斯随机过程模型),并将随机森林引入到SMBO中,来处理分类参数。NNI 的 SMAC tuner 封装了 GitHub 上的 SMAC3参考论文注意:SMAC 算法需要使用 pip install nni[SMAC] 安装依赖,暂不支持 Windows 操作系统。
Batch(批处理)批处理允许用户直接提供若干组配置,为每种配置运行一个 trial。
Grid Search(网格遍历)网格遍历会穷举搜索空间中的所有超参组合。
HyperbandHyperband 试图用有限的资源探索尽可能多的超参组合。该算法的思路是,首先生成大量超参配置,将每组超参运行较短的一段时间,随后抛弃其中效果较差的一半,让较好的超参继续运行,如此重复多轮。参考论文
Metis大多数调参工具仅仅预测最优配置,而 Metis 的优势在于它有两个输出:(a) 最优配置的当前预测结果, 以及 (b) 下一次 trial 的建议。大多数工具假设训练集没有噪声数据,但 Metis 会知道是否需要对某个超参重新采样。参考论文
BOHBBOHB 是 Hyperband 算法的后续工作。 Hyperband 在生成新的配置时,没有利用已有的 trial 结果,而本算法利用了 trial 结果。BOHB 中,HB 表示 Hyperband,BO 表示贝叶斯优化(Byesian Optimization)。 BOHB 会建立多个 TPE 模型,从而利用已完成的 Trial 生成新的配置。参考论文
GP (高斯过程)GP Tuner 是基于序列模型的优化方法 (SMBO),使用高斯过程进行 surrogate。参考论文
PBTPBT Tuner 是一种简单的异步优化算法,在固定的计算资源下,它能有效的联合优化一组模型及其超参来最优化性能。参考论文
DNGODNGO 是基于序列模型的优化方法 (SMBO),该算法使用神经网络(而不是高斯过程)去建模贝叶斯优化中所需要的函数分布。

可以看到示例中,选择的是TPE tuner.

其他的参数比如 trialGpuNumber,指的是使用的gpu数量,trialConcurrency 指的是并发数。trainingService 中 platform 为 local,指的是本地训练。

当然,还有许多参数可以选,比如:

trialConcurrency: 2                 # 同时运行 2 个 trial
maxTrialNumber: 10                  # 最多生成 10 个 trial
maxExperimentDuration: 1h           # 1 小时后停止生成 trial

不过这些参数在调优开始时的web页面上是可以进行调整的。

所以其实NNI干的事情就很清楚了,也很简单。你只需要在你的模型训练文件中增加你想要调优的参数作为输入,就能使用NNI内置的调优算法对不同的参数进行调优,而且允许从页面UI上观察调优的整个过程,相对而言还是很方便的。

不过,NNI可能不太适用一些数据量极大或模型比较复杂的情况。比如基于DDP开发的模型,在NNI中可能无法实现大型的分布式计算。

当然,绝大多数情况下的训练任务,NNI都可以用得上。大家有兴趣深入使用的可以阅读NNI官方文档:https://nni.readthedocs.io/

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

神器!Python 老旧照片的面部恢复模块—GFPGAN

老照片作为时光记忆的载体,不只是过去美好时光的传承者,同时也是每个人的情结和怀念的寄托。

随着时间的流逝,许多老照片都因为自然或人为原因,受到了侵蚀损坏,画面模糊、褪色、照片磨损严重等现象,甚至还有的因为保管不好导致照片面目全非。

今天的这个Python模块叫GFPGAN,它能够让这些老照片恢复原有的光泽,使用了GAN算法对照片进行修复,效果比其他同类模型都有更好的表现。本模块支持Python3.7+版本。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

# 克隆项目
git clone https://github.com/TencentARC/GFPGAN.git
  
# 进入项目
cd GFPGAN

# 安装依赖
pip install basicsr
pip install facexlib
pip install -r requirements.txt
pip install realesrgan

# 安装程序
python setup.py develop

2.使用GFPGAN进行老照片面部恢复

GFPGAN模型需要通过数据集训练得到,由于训练需要使用的数据量和算力非常大,作者团队提供了许多预处理好的模型给普通用户下载,这样我们就能绕过训练这个步骤直接使用模型,下载地址如下:

https://github.com/TencentARC/GFPGAN/releases/download/v0.2.0/GFPGANCleanv1-NoCE-C2.pth

如果你无法访问GitHub,也可以在Python实用宝典后台回复:GFPGAN 下载。包含了本项目源代码及许多其他预训练好的模型,包括:

将想要使用的预训练模型放入 experiments/pretrained_models 文件夹下就可以开始使用了。

使用方法非常简单,进入项目目录后输入以下命令:

python inference_gfpgan.py --model_path experiments/pretrained_models/GFPGANv1.pth --test_path inputs/cropped_faces --save_root results

其中,各个参数的意义如下:

model_path: 使用的模型的位置。

test_path: 需要转换的老照片的路径。

save_root: 转换结果存放的路径。

效果如下:

可见其修复效果是非常优秀的,如果你们也有需要修复的老照片,可以尝试使用手机的照片扫描仪软件扫描后使用此模块修复。

3.微调模型

如果你对模型的输出结果不是很满意,你还可以基于作者团队给出的模型做微调。微调能实现以下目的:

1.如果你有更高质量的人脸数据,可以提高修复效果。

2.你可能需要对数据做一些微处理,比如美妆等。

微调流程如下:

1.准备好训练数据集:https://github.com/NVlabs/ffhq-dataset

2.下载预训练模型和其他你自己的数据,把它们放在 experiments/pretrained_models 文件夹里。我们公众号后台提供以下预训练模型:

  • 预训练的 StyleGAN2 模型:StyleGAN2_512_Cmul1_FFHQ_B12G4_scratch_800k.pth
  • FFHQ 位置:FFHQ_eye_mouth_landmarks_512.pth
  • 一个简单的 ArcFace 模型:arcface_resnet18.pth

3.根据自身需求,相应地修改配置文件 options/train_gfpgan_v1.yml。

4.输入命令训练:

python -m torch.distributed.launch --nproc_per_node=4 --master_port=22021 gfpgan/train.py -opt options/train_gfpgan_v1.yml --launcher pytorch

模型微调的难度比较大,可能会遇到不少问题,大家要善于利用搜索引擎解决问题。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 教你3分钟用Bert搭建问答搜索引擎

鼎鼎大名的 Bert 算法相信大部分同学都听说过,它是Google推出的NLP领域“王炸级”预训练模型,其在NLP任务中刷新了多项记录,并取得state of the art的成绩。

但是有很多深度学习的新手发现BERT模型并不好搭建,上手难度很高,普通人可能要研究几天才能勉强搭建出一个模型。

没关系,今天我们介绍的这个模块,能让你在3分钟内基于BERT算法搭建一个问答搜索引擎。它就是 bert-as-service 项目。这个开源项目,能够让你基于多GPU机器快速搭建BERT服务(支持微调模型),并且能够让多个客户端并发使用。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install bert-serving-server  # 服务端
pip install bert-serving-client  # 客户端

请注意,服务端的版本要求:Python >= 3.5Tensorflow >= 1.10 。

此外还要下载预训练好的BERT模型,在 https://github.com/hanxiao/bert-as-service#install 上可以下载。

也可在Python实用宝典后台回复 bert-as-service 下载这些预训练好的模型。

下载完成后,将 zip 文件解压到某个文件夹中,例如 /tmp/english_L-12_H-768_A-12/.

2.Bert-as-service 基本使用

安装完成后,输入以下命令启动BERT服务:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4 

-num_worker=4 代表这将启动一个有四个worker的服务,意味着它最多可以处理四个并发请求。超过4个其他并发请求将在负载均衡器中排队等待处理。

下面显示了正确启动时服务器的样子:

使用客户端获取语句的编码

现在你可以简单地对句子进行编码,如下所示:

from bert_serving.client import BertClient
bc = BertClient()
bc.encode(['First do it', 'then do it right', 'then do it better'])

作为 BERT 的一个特性,你可以通过将它们与 |||(前后有空格)连接来获得一对句子的编码,例如

bc.encode(['First do it ||| then do it right'])

远程使用 BERT 服务

你还可以在一台 (GPU) 机器上启动服务并从另一台 (CPU) 机器上调用它,如下所示:

# on another CPU machine
from bert_serving.client import BertClient
bc = BertClient(ip='xx.xx.xx.xx')  # ip address of the GPU machine
bc.encode(['First do it', 'then do it right', 'then do it better'])

3.搭建问答搜索引擎

我们将通过 bert-as-service 从FAQ 列表中找到与用户输入的问题最相似的问题,并返回相应的答案。

FAQ列表你也可以在 Python实用宝典后台回复 bert-as-service 下载。

首先,加载所有问题,并显示统计数据:

prefix_q = '##### **Q:** '
with open('README.md') as fp:
    questions = [v.replace(prefix_q, '').strip() for v in fp if v.strip() and v.startswith(prefix_q)]
    print('%d questions loaded, avg. len of %d' % (len(questions), np.mean([len(d.split()) for d in questions])))
    # 33 questions loaded, avg. len of 9

一共有33个问题被加载,平均长度是9.

然后使用预训练好的模型:uncased_L-12_H-768_A-12 启动一个Bert服务:

bert-serving-start -num_worker=1 -model_dir=/data/cips/data/lab/data/model/uncased_L-12_H-768_A-12

接下来,将我们的问题编码为向量:

bc = BertClient(port=4000, port_out=4001)
doc_vecs = bc.encode(questions)

最后,我们准备好接收用户的查询,并对现有问题执行简单的“模糊”搜索。

为此,每次有新查询到来时,我们将其编码为向量并计算其点积 doc_vecs;然后对结果进行降序排序,返回前N个类似的问题:

while True:
    query = input('your question: ')
    query_vec = bc.encode([query])[0]
    # compute normalized dot product as score
    score = np.sum(query_vec * doc_vecs, axis=1) / np.linalg.norm(doc_vecs, axis=1)
    topk_idx = np.argsort(score)[::-1][:topk]
    for idx in topk_idx:
        print('> %s\t%s' % (score[idx], questions[idx]))

完成!现在运行代码并输入你的查询,看看这个搜索引擎如何处理模糊匹配:

完整代码如下,一共23行代码(在后台回复关键词也能下载):

import numpy as np
from bert_serving.client import BertClient
from termcolor import colored

prefix_q = '##### **Q:** '
topk = 5

with open('README.md') as fp:
    questions = [v.replace(prefix_q, '').strip() for v in fp if v.strip() and v.startswith(prefix_q)]
    print('%d questions loaded, avg. len of %d' % (len(questions), np.mean([len(d.split()) for d in questions])))

with BertClient(port=4000, port_out=4001) as bc:
    doc_vecs = bc.encode(questions)

    while True:
        query = input(colored('your question: ', 'green'))
        query_vec = bc.encode([query])[0]
        # compute normalized dot product as score
        score = np.sum(query_vec * doc_vecs, axis=1) / np.linalg.norm(doc_vecs, axis=1)
        topk_idx = np.argsort(score)[::-1][:topk]
        print('top %d questions similar to "%s"' % (topk, colored(query, 'green')))
        for idx in topk_idx:
            print('> %s\t%s' % (colored('%.1f' % score[idx], 'cyan'), colored(questions[idx], 'yellow')))

够简单吧?当然,这是一个基于预训练的Bert模型制造的一个简单QA搜索模型。

你还可以微调模型,让这个模型整体表现地更完美,你可以将自己的数据放到某个目录下,然后执行 run_classifier.py 对模型进行微调,比如这个例子:

https://github.com/google-research/bert#sentence-and-sentence-pair-classification-tasks

它还有许多别的用法,我们这里就不一一介绍了,大家可以前往官方文档学习:

https://github.com/hanxiao/bert-as-service

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Pytorch+spark 进行大规模预测

虽然深度学习日益盛行,但目前spark还不支持深度学习算法。虽然也有相关库sparktorch能够将spark和pytorch结合起来,但是使用发现并非那么好用,而且此库目前活跃度较低,不方便debug。因此,本地训练深度学习模型并部署到spark中是一种有效的利用深度学习进行大规模预测的方法。

将pytorch模型嵌入部署到spark中进行大规模预测主要包括三步:

1.利用spark进行特征工程预处理,以保证训练集和测试集特征处理一致;

第一二步都比较简单,这里省去。主要对第三步进行说明。

模型分发(broadcast)分两种情况,第一种是简单可通过nn.Sequential定义的模型。对于这种情况可以,模型可以直接用。如下:

# 生成测试数据
from sklearn.datasets import make_classification

X, y = make_classification(n_samples=50000, n_features=100, random_state=0)
df = pd.DataFrame(X)
df['label'] = np.random.randint(2,size=(50000))
df1 = spark.createDataFrame(df)
df1 = df1.withColumn('features', Func.array([col(f"{i}") for i in range(0, 100)])).repartition(1000)

# 创建模型并进行预测
%spark2_1.pyspark
import torch.nn as nn

network = nn.Sequential(
    nn.Linear(100, 2560),
    nn.ReLU(),
    nn.Linear(2560, 2560),
    nn.ReLU(),
    nn.Linear(2560, 2)
    #nn.Softmax(dim=1)
)

class network(nn.Module):
    def __init__(self):
        super(network, self).__init__()
        self.l1 = nn.Linear(100, 2560)
        self.l2 = nn.Linear(2560, 2560)
        self.l3 = nn.Linear(2560, 2)
        
    def forward(self, x):
        x = self.l1(x)
        x = self.l2(x)
        x = self.l3(x)
        return x

net = network()
bc_model_state = spark.sparkContext.broadcast(net.state_dict())

def get_model_for_eval():
  # Broadcast the model state_dict
  net.load_state_dict(bc_model_state.value)
  net.eval()
  return net
  
def one_row_predict(x):
    model = get_model_for_eval()
    t = torch.tensor(x, dtype=torch.float32)
    t = model(t).cpu().detach().numpy()
    #prediction = model(t).cpu().detach().item()
    # return prediction
    return list([float(i) for i in t])

one_row_udf = udf(one_row_predict, ArrayType(FloatType()))
df1 = df1.withColumn('pred_one_row', one_row_udf(col('features')))

在上面我们定义了一个简单模型,然后将其直接分发进行预测(这里省去了模型训练过程)。

但是当我们想使用一个比较复杂的模型来进行预测时(简单来讲就是不能使用 nn.Sequential 改写),使用上面的方法则会报错。

这时候需要将模型写入一个文件中,假设模型文件的路径为/export/models/item2vec.py, 使用pyspark中的addFile对其进行分发,然后import导入模型。

假设我们的模型文件/export/models/item2vec.py如下:

class Item2vec(nn.Module):
    def __init__(self, cv_dict, csr_cols):
        super(Item2vec, self).__init__()
        pass

    def forward(self, x):
        pass

    def predict(self, x):
        pass

假设模型已经训练好,现在要使用训练好的模型进行大规模预测:

from pyspark import SparkFiles
sc.addFile('/export/models/item2vec.py')
import sys
sys.path.append('/export/models/')

from item2vec import Item2vec

# model 表示训练好的模型
bc_model_state = sc.broadcast(model.state_dict())
net = Item2vec(cv_dict, csr_cols)

def get_model_for_eval_demo():
  # Broadcast the model state_dict
  net.load_state_dict(bc_model_state.value)
  net.eval()
  return net

上面的操作已经将模型分发(broadcast)出去,接下来就可以进行预测了。

预测这里介绍两种方式:一种是使用 udf + withColumn, 另一种则是使用 rdd + mapPartitions

由于这里使用的是 pyspark 2.1,还没有pandas udf,因此使用 udf + withColumn 时只能一行一行的预测,运行速度上来说是比不上 rdd + mapPartitions

对于pyspark 2.3以后的版本多了pandas udf后则可以使用batch predict了,具体可以参考

https://docs.databricks.com/static/notebooks/deep-learning/pytorch-images.html

udf + withColumn 的方式
# udf + withColumn 的方式
def one_row_predict_demo(x)
    x = torch.tensor(x, dtype=torch.float)
    _, prob = bc_model.predict(x)

    return round(float(prob[0]), 4)
    
one_row_predict_demo_udf = udf(one_row_predict_demo, DoubleType())

one_row_predict_demo_udf = udf(one_row_predict_demo, DoubleType())
df = demo.withColumn('demo_prob', one_row_predict_demo_udf('features'))
rdd + map 方式
def one_row_predict_map(rdds):
    bc_model = get_model_for_eval_demo()
    for row in rdds:
        x = torch.tensor(row.x, dtype=torch.float)
        _, prob = bc_model.predict(x)
    
        yield (row['id'], round(float(prob[0]), 4))

df = demo.rdd.mapPartitions(one_row_predict_map).toDF(['id', 'pred_prob'])

2. 效率优化(1)——mapPartition

上面的方法已经可以使得我们将训练好的深度学习模型部署到spark进行大规模预测了,但是其速度是非常慢的。通过在 mapPartitions 中进行一些处理,我们可以对预测进行加速:

# 代码源自 https://github.com/SaeedNajafi/infer-pytorch-pyspark

def basic_row_handler(row):
    return row

def predict_map(index, partition, ml_task,
                batch_size=16,
                row_preprocessor=basic_row_handler,
                row_postprocessor=basic_row_handler):

    # local model loading within each executor
    model = LocalPredictor(ml_task=ml_task, batch_size=batch_size,
                           partition_index=index)

    batch = []
    count = 0
    for row in partition:
        row_dict = row.asDict()
        # apply preprocessor on each row.
        row_dict_prep = row_preprocessor(row_dict)
        batch.append(row_dict_prep)
        count += 1
        if count == batch_size:
            # predict the ml and apply the postprocessor.
            for ret_row in model.predict(batch):  # ml prediction
                ret_row_post = row_postprocessor(ret_row)
                if ret_row_post is not None:
                    yield Row(**ret_row_post)

            batch = []
            count = 0

    # Flush remaining rows in the batches.
    if count != 0:
        for ret_row in model.predict(batch):  # ml prediction
            ret_row_post = row_postprocessor(ret_row)
            if ret_row_post is not None:
                yield Row(**ret_row_post)

        batch = []
        count = 0

上面的代码可以看作是在mapPartitions中进行了“延迟”预测——即先将一个partition中的多行数据进行处理然后合并为一个batch进行一起预测,这样能大大的提升运行效率。一个比较极端的情况是每个partition仅进行一次预测。

3. 效率优化(2)——pandas_udf

pandas_udf在udf的基础上进行了进一步的优化,利用pandas_udf程序运行效率更高。在这里我们可以借助于pandas_udf提升我们程序的运行效率:

# Enable Arrow support.
spark.conf.set("spark.sql.execution.arrow.enabled", "true")
spark.conf.set("spark.sql.execution.arrow.maxRecordsPerBatch", "64")

sc.addFile('get_model.py')
from get_model import get_model

model_path = '/path/to/model.pt'
data_path = '/path/to/data'

# model 表示训练好的模型
model = torch.load(model_path)
bc_model_state = sc.broadcast(model.state_dict())


def get_model_for_eval():
  # Broadcast the model state_dict  
  model = get_model()
  model.load_state_dict(bc_model_state.value)
  model.eval()
  return model

# model = torch.load(model_path)
# model = sc.broadcast(model)


@pandas_udf(FloatType())
def predict_batch_udf(arr: pd.Series) -> pd.Series:
    model = get_model_for_eval()
    # model.to(device)
    arr = np.vstack(arr.map(lambda x: eval(x)).values)
    arr = torch.tensor(arr).long()
    with torch.no_grad():
        predictions = list(model(arr).cpu().numpy())
            
    return pd.Series(predictions)

# 预测
data = data.withColumn('predictions', predict_batch_udf('features'))

作者:井底蛙蛙呱呱呱
链接:https://www.jianshu.com/p/fc60c967c8b8

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python paddleseg 图像分割实战教程 — 眼底识别

在人工智能领域中,有一项非常关键的技术,那就是图像分割

图像分割是指将图像中具有特殊意义的不同区域划分开来, 这些区域互不相交,每个区域满足灰度、纹理、彩色等特征的某种相似性准则。

比如上图识别视盘。视盘是视网膜中的关键解剖学结构,其形状、面积和深度等参数是衡量眼底健康状况的重要指标,准确定位和分割视盘区域是眼底图像分析和处理的关键步骤。

在人工智能的辅助下,只需要数秒,即可初步判断被检者是否存在眼底疾病,这将有助缓解专业眼科医生不足的瓶颈,开启眼底疾病的基层筛查新模式。而图像分割就是实现这项功能的基础,可见其重要性。

下面就给大家讲讲如何基于 PaddlePaddle 平台,训练并测试一个视盘图像分割的基本模型。

1.Python 准备

为了实现这个实验,Python 是必不可少的,如果你还没有安装 Python,建议阅读我们的这篇文章:超详细Python安装指南

在安装前,确认自己需要的 PaddlePaddle 版本,比如 GPU版 或 CPU版,GPU 在计算上具有绝对优势,但是如果你没有一块强力的显卡,建议选择CPU版本。

(GPU版) 如果你想使用GPU版,请确认本机安装了 CUDA 计算平台及 cuDNN,它们的下载地址分别是:
https://developer.nvidia.com/cuda-downloads
https://developer.nvidia.com/cudnn-download-survey

具体 CUDA 和 cuDNN 对应的版本要求如下:

  • CUDA 工具包10.1/10.2配合cuDNN v7.6+
  • CUDA 工具包11.2配合cuDNN v8.1.1

CUDA安装流程很简单,下载exe程序,一路往下走。cuDNN安装流程复杂一些,你需要转移压缩包解压后的部分文件到CUDA中,具体可见这篇cuDNN的官方安装指引:
https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html

(CPU版)CPU版安装过程比较简单,直接按照下面 PaddlePaddle 的安装指引输入命令即可。


(通用)选择完你想要安装的版本,并做好基础工作后,接下来就是安装 PaddlePaddle 的具体步骤,打开安装指引流程页面:
https://www.paddlepaddle.org.cn/install/quick

根据你自己的情况选择这些选项,最后一个选项计算平台指的是 GPU 加速工具包或CPU,如果你不想用GPU,请选CPU版;想用GPU版的同学请按刚刚下载的CUDA版本进行选择。

选择完毕后下方会出现安装信息,输入安装信息里的命令即可安装成功,不得不说,PaddlePaddle 这些方面做的还是比较贴心的。

在页面下方还有具体的从头到尾的安装步骤,对 Python 基本的虚拟环境安装流程不了解的同学可以看着这些步骤进行安装。

2.初尝paddleseg图像分割

安装完 paddle 后,为了能够实现图像分割功能,我们还需要安装 paddleseg:

pip install paddleseg

并克隆 paddleseg的代码库(如果克隆不了,请在Python实用宝典公众号后台回复:图像分割 下载):

git clone https://github.com/PaddlePaddle/PaddleSeg.git

克隆完成,进入代码库文件夹:

cd PaddleSeg

执行下面命令,并在 PaddleSeg/output 文件夹中出现预测结果,则证明安装成功。

python predict.py \
       --config configs/quick_start/bisenet_optic_disc_512x512_1k.yml \
       --model_path https://bj.bcebos.com/paddleseg/dygraph/optic_disc/bisenet_optic_disc_512x512_1k/model.pdparams\
       --image_path docs/images/optic_test_image.jpg \
       --save_dir output/result

预测结果如下:

3.训练模型

前面只是利用了 PaddlePaddle 提前训练好的数据进行预测,下面我们要尝试自己训练一个模型。

为了训练模型,我们需要获得眼底训练集。事实上,在前面 初尝 Paddleseg 中,我们便获得了一份眼底训练集,其路径是 PaddleSeg\data\optic_disc_seg.

如果你没有进行 初尝 Paddleseg 这一节,也想要获取训练集数据的话,在Python实用宝典公众号后台回复:图像分割 下载。下载后解压数据集,得到一个optic_disc_seg文件夹,将其放到 PaddleSeg 代码库的 data 文件夹下。

配置化训练

PaddleSeg 提供了配置化驱动进行模型训练。他们在配置文件中详细列出了每一个可以优化的选项,用户只要修改这个配置文件就可以对模型进行定制。

所有的配置文件在PaddleSeg/configs文件夹下面

每一个文件夹代表一个模型,里面包含这个模型的所有配置文件。

在PaddleSeg的配置文件给出的学习率中,除了”bisenet_optic_disc_512x512_1k.yml”中为单卡学习率外,其余配置文件中均为4卡的学习率,因此如果你是单卡训练,则学习率设置应变成原来的1/4。

为了简化学习难度,我们继续以”bisenet_optic_disc_512x512_1k.yml”文件为例,修改部分参数进行训练,下面是这个配置的全部说明:

batch_size: 4  #设定batch_size的值即为迭代一次送入网络的图片数量,一般显卡显存越大,batch_size的值可以越大
iters: 1000    #模型迭代的次数

train_dataset: #训练数据设置
  type: OpticDiscSeg #选择数据集格式
  dataset_root: data/optic_disc_seg #选择数据集路径
  num_classes: 2 #指定目标的类别个数(背景也算为一类)
  transforms: #数据预处理/增强的方式
    - type: Resize #送入网络之前需要进行resize
      target_size: [512, 512] #将原图resize成512*512在送入网络
    - type: RandomHorizontalFlip #采用水平反转的方式进行数据增强
    - type: Normalize #图像进行归一化
  mode: train

val_dataset: #验证数据设置
  type: OpticDiscSeg #选择数据集格式
  dataset_root: data/optic_disc_seg #选择数据集路径
  num_classes: 2 #指定目标的类别个数(背景也算为一类)
  transforms: #数据预处理/增强的方式
    - type: Resize  #将原图resize成512*512在送入网络
      target_size: [512, 512]  #将原图resize成512*512在送入网络
    - type: Normalize #图像进行归一化
  mode: val

optimizer: #设定优化器的类型
  type: sgd #采用SGD(Stochastic Gradient Descent)随机梯度下降方法为优化器
  momentum: 0.9 #动量
  weight_decay: 4.0e-5 #权值衰减,使用的目的是防止过拟合

learning_rate: #设定学习率
  value: 0.01  #初始学习率
  decay:
    type: poly  #采用poly作为学习率衰减方式。
    power: 0.9  #衰减率
    end_lr: 0   #最终学习率

loss: #设定损失函数的类型
  types:
    - type: CrossEntropyLoss #损失函数类型
  coef: [1, 1, 1, 1, 1]
  #BiseNetV2有4个辅助loss,加上主loss共五个,1表示权重 all_loss = coef_1 * loss_1 + .... + coef_n * loss_n

model: #模型说明
  type: BiSeNetV2  #设定模型类别
  pretrained: Null #设定模型的预训练模型

你可以尝试调整部分参数进行训练,看看你自己训练的模型效果和官方给出的模型的效果的差别。

开始训练

(GPU版)在正式开启训练前,我们需要将CUDA设置为目前有1张可用的显卡:

set CUDA_VISIBLE_DEVICES=0 # windows
# export CUDA_VISIBLE_DEVICES=0 # linux

输入训练命令开始训练:

python train.py \
       --config configs/quick_start/bisenet_optic_disc_512x512_1k.yml \
       --do_eval \
       --use_vdl \
       --save_interval 500 \
       --save_dir output
训练参数解释
参数名用途是否必选项默认值
iters训练迭代次数配置文件中指定值
batch_size单卡batch size配置文件中指定值
learning_rate初始学习率配置文件中指定值
config配置文件
save_dir模型和visualdl日志文件的保存根路径output
num_workers用于异步读取数据的进程数量, 大于等于1时开启子进程读取数据0
use_vdl是否开启visualdl记录训练数据
save_interval_iters模型保存的间隔步数1000
do_eval是否在保存模型时启动评估, 启动时将会根据mIoU保存最佳模型至best_model
log_iters打印日志的间隔步数10
resume_model恢复训练模型路径,如:output/iter_1000None
keep_checkpoint_max最新模型保存个数5

见到如下的界面,说明你已经开始训练了:

4.训练过程可视化

PaddlePaddle 还提供了可视化分析工具:VisualDL,让我们的网络训练过程更加直观。

当打开use_vdl开关后,PaddleSeg会将训练过程中的数据写入VisualDL文件,可实时查看训练过程中的日志。记录的数据包括:

  1. loss变化趋势
  2. 学习率变化趋势
  3. 训练时间
  4. 数据读取时间
  5. mean IoU变化趋势(当打开了do_eval开关后生效)
  6. mean pixel Accuracy变化趋势(当打开了do_eval开关后生效)

使用如下命令启动VisualDL查看日志

# 下述命令会在127.0.0.1上启动一个服务,支持通过前端web页面查看,可以通过--host这个参数指定实际ip地址
visualdl --logdir output/

在浏览器输入提示的网址,效果如下:

如图所示,打开 http://127.0.0.1:8040/ 页面,效果如下:

5.模型测试评估

训练完成后,用户可以使用评估脚本val.py来评估模型效果。

假设训练过程中迭代次数(iters)为1000,保存模型的间隔为500,即每迭代1000次数据集保存2次训练模型。

因此一共会产生2个定期保存的模型,加上保存的最佳模型best_model,一共有3个模型,可以通过model_path指定期望评估的模型文件。

python val.py \
       --config configs/quick_start/bisenet_optic_disc_512x512_1k.yml \
       --model_path output/iter_1000/model.pdparams

在图像分割领域中,评估模型质量主要是通过三个指标进行判断,准确率(acc)、平均交并比(Mean Intersection over Union,简称mIoU)、Kappa系数。

  • 准确率:指类别预测正确的像素占总像素的比例,准确率越高模型质量越好。
  • 平均交并比:对每个类别数据集单独进行推理计算,计算出的预测区域和实际区域交集除以预测区域和实际区域的并集,然后将所有类别得到的结果取平均。在本例中,正常情况下模型在验证集上的mIoU指标值会达到0.80以上,显示信息示例如下所示,第2行的mIoU=0.8609即为mIoU。
  • Kappa系数:一个用于一致性检验的指标,可以用于衡量分类的效果。Kappa系数越高模型质量越好。

随着评估脚本的运行,最终打印的评估日志如下。

76/76 [==============================] - 6s 84ms/step - batch_cost: 0.0835 - reader cost: 0.0029
2021-06-05 19:38:53 [INFO]      [EVAL] #Images: 76 mIoU: 0.8609 Acc: 0.9945 Kappa: 0.8393
2021-06-05 19:38:53 [INFO]      [EVAL] Class IoU:
[0.9945 0.7273]
2021-06-05 19:38:53 [INFO]      [EVAL] Class Acc:
[0.9961 0.8975]

可以看到,我改了参数后的训练效果还是不错的。

6.效果可视化

除了分析模型的IOU、ACC和Kappa指标之外,我们还可以查阅一些具体样本的切割样本效果,从Bad Case启发进一步优化的思路。

predict.py脚本是专门用来可视化预测案例的,命令格式如下所示

python predict.py \
       --config configs/quick_start/bisenet_optic_disc_512x512_1k.yml \
       --model_path output/iter_1000/model.pdparams \
       --image_path data/optic_disc_seg/JPEGImages/H0003.jpg \
       --save_dir output/result

运行完成后,打开 output/result 文件夹。我们选择1张图片进行查看,效果如下。

我们可以直观的看到模型的切割效果和原始标记之间的差别,从而产生一些优化的思路,比如是否切割的边界可以做规则化的处理等。

大家也可以尝试自己标注一个数据集进行图像分割,你只要按照 PaddleSeg\data\optic_disc_seg 里面那样组织图片结构,就可以复用这些训练、评估的过程。

本文部分内容摘自: PaddleSeg官方文档

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 将你的照片转化为“速写”

Photo-Sketching 一个能将照片的轮廓识别出来并将其转化为“速写”型图像的开源模块。

比如,这只小狗:

经过模型的转化,会变成卡通版的小狗:

当然,也不是什么照片都处理的好,比如这个风景画就不行:

摇身一变,成了抽象风格:

非常好,这很人工智能。

这个模块的使用也相对简单,下面给大家带上全方面的教程:

1.虚拟环境及依赖安装

这个项目推荐大家直接用Anaconda进行环境的构建和开发:Python数据分析与挖掘好帮手—Anaconda,因为作者提供了一个 environment.yml 文件,你只需要输入以下命令,就能一键安装环境和依赖:

conda env create -f environment.yml

此外,推荐大家用VSCode编辑器来编写像这样的小型Python项目:Python 编程的最好搭档—VSCode 详细指南

2.下载预训练模型

作者已经训练好了一些识别模型方便大家使用,可以在下列地址找到:
https://drive.google.com/file/d/1TQf-LyS8rRDDapdcTnEgWzYJllPgiXdj/view

作者使用的是谷歌硬盘,如果你无法科学上网,可以使用我提供的完整源代码+预训练模型,在后台回复:sketch 即可获取。

下载完成后解压文件,将 latest_net_D.pth 和 latest_net_G.pth 放置到 Checkpoints 文件夹下:

3.运行预训练模型

接下来,我们需要修改使用预训练模型的启动脚本,这些脚本都放在 PhotoSketch\scripts 下,我们需要使用的是 test_pretrained.cmd 或者 test_pretrained.sh 这两个脚本。

如果你是 windows 系统,请修改 test_pretrained.cmd 脚本,重点是dataDir、results_dir、checkpoints_dir:

dataDir 指向到 PhotoSketch 所在的文件夹目录,如果你是跟我一样这么配的,results_dir 只需要配成 %dataDir%\PhotoSketch\Results\ 即可,checkpoints_dir 则为 %dataDir%\PhotoSketch\Checkpoints\

如果你是macOS或者Linux,则修改 test_pretrained.sh 文件,修改方法与上面windows 的一样,只不过 反斜杠 “\” 要换成 斜杆 “/” 。

修改完脚本后,打开命令行/终端,输入以下命令,就会将你 PhotoSketch\examples 目录下的文件转化为“速写”。

windows:

scripts\test_pretrained.cmd

Linux/Macos:

./scripts/test_pretrained.sh

转化结果可以在 PhotoSketch\Results 中看到,如下两图所示。

待转化目录:

转化后:

可以看到效果其实不是非常好,由于是作者预训练的模型,所以效果不好也正常,如果大家需要的话,可以自己针对性地拿一些图像训练模型,并针对性地做识别,这样做效果才是最好的。

你需要训练或测试自己的模型也非常简单:

  • 在仓库的根目录中,运行 scripts/train.sh 可以训练模型
  • 在仓库的根目录中,运行 scripts/test.sh 可以测试val集或测试集

当然训练过程肯定没这么简单,你会遇到不少问题,但是我相信大部分都是存放图片的目录结构上的问题,大家如果有兴趣可以动手试试。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

超简单教你用Python克隆卷福的声音

语音克隆是这两年比较火的深度学习应用,它允许从几秒钟的音频中学习对象的说话方式和音调,并使用它来生成新的语音。

下面来看看我使用 SV2TTS 训练模仿卷福阅读下面这句话的效果:

She is beginning to get many wrinkles around her eyes.

训练集:

克隆、模仿效果(She is beginning to get many wrinkles around her eyes.):

https://pythondict-1252734158.file.myqcloud.com/home/www/pythondict/wp-content/uploads/2021/03/2021031017113638.wav

效果不错,如果不知道它是生成的,还以为真的是卷福念的。

下面就来教大家如何使用 Real-Time-Voice-Cloning 项目克隆语音并生成自己想要的语句。

1.准备

大家可以前往 Real-Time-Voice-Cloning 项目下载这个项目的代码以及预训练完成的模型。(注意,需要Python 3.6以上才能运行该项目)

如果你的网络速度比较差,下载不了 github 项目及其预训练模型,可以在 Python 实用宝典 公众号后台回复 克隆语音 下载完整项目代码及预训练模型。

下载完项目代码后,你还需要下载两个重要依赖:

安装 PyTorch

其中,PyTorch的官方指南已经写得很清楚了,大家根据自己的需求安装即可。

安装 ffmpeg

而 ffmpeg 的安装我们已经在这篇文章详细地讲过:Python 多种音乐格式转换(批量)实战教程,在此重新讲解一下:

Mac (打开终端(Terminal), 用 homebrew 安装):

brew install ffmpeg --with-libvorbis --with-sdl2 --with-theora

Linux:

apt-get install ffmpeg libavcodec-extra

Windows:

1. 进入 http://ffmpeg.org/download.html#build-windows,点击 windows 对应的图标,进入下载界面点击 download 下载按钮,
2. 解压下载好的zip文件到指定目录
3. 将解压后的文件目录中 bin 目录(包含 ffmpeg.exe )添加进 path 环境变量中

安装模块依赖

安装完成以上两个重要依赖后,在终端、命令行中进入项目目录中,安装依赖:

pip install -r requirements.txt

这会安装所有 requirements.txt 中的所有依赖。

2.下载预训练模型(可选)

如果你用的是我们提供的项目文件,你就不需要再进行这一步了,因为把预训练的模型都已经放进去了。

如果你没有用Python实用宝典提供的项目代码,你还需要去下载预训练的模型:

https://github.com/CorentinJ/Real-Time-Voice-Cloning/wiki/Pretrained-models

下载完成后解压 pretrained.zip 分别将对应的模型放入项目对应的位置中:

encoder\saved_models\pretrained.pt
synthesizer\saved_models\pretrained\pretrained.pt
vocoder\saved_models\pretrained\pretrained.pt

3.试一下克隆语音

随便选取一段你想要克隆的人的语音,大概30秒左右,放入项目文件夹中。然后在该文件夹中运行命令:

python demo_cli.py

如果一切正常,它会出现让你选择训练语音文件:

此时输入你准备好的一段语音,等待它训练完成后,它会让你输入想要模仿的文字:

比如上图中,我输入了:

She is beginning to get many wrinkles around her eyes.

程序生成完毕后会自动念出克隆结果,如果你没有听见克隆结果,没关系,程序会将其保存在当前文件夹下,命名为 demo_output_xx.wav.

双击打开这个文件,就是它生成的语音克隆结果啦,听听看,是不是你想要的效果?

如果没有达到你的理想效果,请检查一下训练集是否有杂音、时间够不够长、有没有其他人的介入,这些因素都可能导致克隆效果不理想。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典