基于docker的tensorflow2 bert 新闻分类模型部署

人工智能87

整体思路:
1)使用tensorflow2加载预训练bert模型,进行训练,然后将模型部署载tfseving中。
2)使用flask部署模型推理,模型推理时会requrest请求 1) 中tfserving部署的模型
3) 将2)的flask构建成一个docker镜像,运行镜像即启动了服务
4)模型更新:只需将最新模型放入tfserving的对应模型路径下

1 模型训练及tfserving部署

参考博客

https://blog.csdn.net/weixin_42529756/article/details/122420339?spm=1001.2014.3001.5501

2 模型推理及flask部署

模型整理文件如下:

  1. requirements.txt #由于使用原生的Python构建dockeer镜像,需要安装很多依赖包。
  2. flask_infer.py # 模型推理文件,如下所示
  3. bert-case-chinese # Google须训练bert模型,(只需要里面的配置文件即可,不需要模型,主要用来分词tokenizer)

注意: app.run(port=5000,host="0.0.0.0")
中的host不要使用默认的,不然远程会访问不了的。

flask_infer.py文件如下

from flask import Flask,request,jsonify
app=Flask(__name__)
from transformers import BertTokenizer
import pandas as pd
import json
import requests
import numpy as np
model_path = "./bert-case-chinese/"
max_length=50
df_label = pd.DataFrame({"label":["财经","房产","股票","教育","科技","社会","时政","体育","游戏","娱乐"],"y":list(range(10))})
new_label=df_label.to_dict()['label']

tokenizer = BertTokenizer.from_pretrained(model_path )
headers = {"content-type": "application/json"}
def predict(text):
    input_dict = tokenizer(text, return_tensors='tf',max_length=max_length,padding  ='max_length')
    input_ids = input_dict["input_ids"].numpy().tolist()[0]
    attention_mask = input_dict["attention_mask"].numpy().tolist()[0]
    token_type_ids = input_dict["token_type_ids"].numpy().tolist()[0]

    features = [{'input_ids': input_ids, 'attention_mask': attention_mask,'token_type_ids':token_type_ids}]

    data = json.dumps({ "signature_name": "serving_default", "instances": features})

    json_response = requests.post('http://192.168.10.100:8501/v1/models/model:predict', data=data, headers=headers)

    predictions = json.loads(json_response.text)['predictions']
    return predictions
app.config['JSON_AS_ASCII'] = False
@app.route('/hello')
def hello():
    return "hello world "

@app.route('/predict',methods=['POST'])
def pred():
    try:
        data=request.get_json()['data']
        print("data," ,data)
        predictions=predict(data)
        print("predictions : ",predictions)
        label_to_index=np.argmax(predictions)
        index_to_label=new_label[label_to_index]

        response={"label":index_to_label,
                 "response_state":"success"}
    except:
        response={"reponse_state":"error"}
    return jsonify(response)
if __name__=='__main__':
    app.run(port=5000,host="0.0.0.0")

requirements.txt,内容如下

transformers==4.12.5
flask==2.0.1
numpy==1.19.5
pandas==1.1.2
tensorflow==2.4.1

3 docker镜像创建及运行

1)使用上面的文件构建Dockerfile镜像
基于docker的tensorflow2 bert 新闻分类模型部署

2)Dockerfile 文件内容

FROM python:3.6
WORKDIR /opt
RUN mkdir -p /opt/serving/bertclf
COPY . /opt
RUN pip install -r requirements.txt -i  https://pypi.tuna.tsinghua.edu.cn/simple
ENTRYPOINT ["python"]
CMD ["flask_infer.py"]

3)镜像构建

docker build -t my_docker_flask:1.1 .

基于docker的tensorflow2 bert 新闻分类模型部署

4) 容器运行

 docker run -it -p 5000:5000 my_docker_flask:1.1

基于docker的tensorflow2 bert 新闻分类模型部署

5)请求
基于docker的tensorflow2 bert 新闻分类模型部署

Original: https://blog.csdn.net/weixin_42529756/article/details/122522556
Author: 晚点吧
Title: 基于docker的tensorflow2 bert 新闻分类模型部署



相关阅读1

Title: 【SLAM】LIO-SAM解析——数据预处理imageProjection(2)

知识点:
如何用IMU的角加速度对lidar帧去旋转畸变,如何用里程计的平移数据对lidar帧去平移畸变,如何用IMU和里程计初始时刻的位姿给这一帧找到位姿。

这部分内容对应imageProjection.cpp,雷达帧是一定角度旋转的(例如360°),同时机器人也在运动,这就造成了雷达在不同角度获取数据时机器人的位姿是不同的,也就是说,lidar在一帧时间范围内的每一个时刻所在的坐标系是不同的,对应获得的点云也是在不同坐标系下的。
这部分代码的作用就是把雷达旋转一周时,所有的点云都转换到同一个位姿下,也就是该帧初始时刻的lidar坐标系下。如何做到呢?初始时刻的位姿我知道,起止时间内的位姿变化我知道,每一个点云的时刻我知道,那么每一个时刻相对于初始时刻的位姿变化(平移有里程计变化量获得,旋转角由IMU变化量获得)就可以根据时间线性插值获得,然后根据这个位姿变化量把点变换过去就行了。
对每一个激光点云都这么玩一下,就实现了lidar帧的运动去畸变。

int main(int argc, char** argv)
{
    ros::init(argc, argv, "lio_sam");
    ImageProjection IP;

    ROS_INFO("\033[1;32m----> Image Projection Started.\033[0m");

    ros::MultiThreadedSpinner spinner(3);
    spinner.spin();

    return 0;
}

核心内容就在创建ImageProjection对象时进行的,看一下他的构造函数:

    ImageProjection():deskewFlag(0)
    {
        // 订阅原始imu数据
        subImu        = nh.subscribe(imuTopic, 2000, &ImageProjection::imuHandler, this, ros::TransportHints().tcpNoDelay());
        // 订阅imu里程计,由imuPreintegration积分计算得到的每时刻imu位姿
        subOdom       = nh.subscribe(odomTopic+"_incremental", 2000, &ImageProjection::odometryHandler, this, ros::TransportHints().tcpNoDelay());
        // 订阅原始lidar数据
        subLaserCloud = nh.subscribe(pointCloudTopic, 5, &ImageProjection::cloudHandler, this, ros::TransportHints().tcpNoDelay());

        // 发布当前激光帧运动畸变校正后的点云,有效点
        pubExtractedCloud = nh.advertise ("lio_sam/deskew/cloud_deskewed", 1);
        // 发布当前激光帧运动畸变校正后的点云信息
        pubLaserCloudInfo = nh.advertise ("lio_sam/deskew/cloud_info", 1);

        // 初始化
        allocateMemory();
        // 重置参数
        resetParameters();

        // pcl日志级别,只打ERROR日志
        pcl::console::setVerbosityLevel(pcl::console::L_ERROR);
    }

在这里可以看到,在对象构造的时候,就创建了ROS的回调函数,这里有三个关键的函数,分别是imuHandler(),odometryHandler()和cloudHandler(),分别对应IMU数据,里程计数据和雷达数据。

2.1 回调函数imuHandler()

这部分作用是把IMU数据的坐标系转换到lidar系上,从而和lidar数据进行匹配,然后放到容器里去。

    /**
     * 订阅原始imu数据
     * 1、imu原始测量数据转换到lidar系,加速度、角速度、RPY
    */
    void imuHandler(const sensor_msgs::Imu::ConstPtr& imuMsg)
    {
        // imu原始测量数据转换到lidar系,加速度、角速度、RPY
        sensor_msgs::Imu thisImu = imuConverter(*imuMsg);

        // 上锁,添加数据的时候队列不可用
        std::lock_guard lock1(imuLock);
        imuQueue.push_back(thisImu);
    }

imuConverter()定义在utility.h文件里,这个文件定义了全部通用的工具函数。它把线加速度和角速度转化到lidar系下,rot转化为从lidar->world的旋转。

    /**
     * imu原始测量数据转换到lidar系,加速度、角速度、RPY
    */
    sensor_msgs::Imu imuConverter(const sensor_msgs::Imu& imu_in)
    {
        sensor_msgs::Imu imu_out = imu_in;
        // 加速度,只跟xyz坐标系的旋转有关系
        Eigen::Vector3d acc(imu_in.linear_acceleration.x, imu_in.linear_acceleration.y, imu_in.linear_acceleration.z);
        acc = extRot * acc;
        imu_out.linear_acceleration.x = acc.x();
        imu_out.linear_acceleration.y = acc.y();
        imu_out.linear_acceleration.z = acc.z();
        // 角速度,只跟xyz坐标系的旋转有关系
        Eigen::Vector3d gyr(imu_in.angular_velocity.x, imu_in.angular_velocity.y, imu_in.angular_velocity.z);
        gyr = extRot * gyr;
        imu_out.angular_velocity.x = gyr.x();
        imu_out.angular_velocity.y = gyr.y();
        imu_out.angular_velocity.z = gyr.z();
        // RPY
        Eigen::Quaterniond q_from(imu_in.orientation.w, imu_in.orientation.x, imu_in.orientation.y, imu_in.orientation.z);
        Eigen::Quaterniond q_final = q_from * extQRPY;
        imu_out.orientation.x = q_final.x();
        imu_out.orientation.y = q_final.y();
        imu_out.orientation.z = q_final.z();
        imu_out.orientation.w = q_final.w();

        if (sqrt(q_final.x()*q_final.x() + q_final.y()*q_final.y() + q_final.z()*q_final.z() + q_final.w()*q_final.w()) < 0.1)
        {
            ROS_ERROR("Invalid quaternion, please use a 9-axis IMU!");
            ros::shutdown();
        }

        return imu_out;
    }

2.2 回调函数odometryHandler()

直接把里程计放到buffer里去,注意这里接收的里程计都已经是lidar系在world系下的表示 T(world

Original: https://blog.csdn.net/iwanderu/article/details/123058727
Author: iwander。
Title: 【SLAM】LIO-SAM解析——数据预处理imageProjection(2)

相关阅读2

Title: React中props.children和React.Children的区别

在React中,当涉及组件嵌套,在父组件中使用 props.children把所有子组件显示出来。如下:

function ParentComponent(props){
    return (
        <div>
            {props.children}
        </div>
    )
}

如果想把父组件中的属性传给所有的子组件,该怎么做呢?

--使用 React.Children帮助方法就可以做到。

比如,把几个Radio组合起来,合成一个RadioGroup,这就要求所有的Radio具有同样的name属性值。可以这样设计:把Radio看做子组件,RadioGroup看做父组件,name的属性值在RadioGroup这个父组件中设置。

首先是子组件:

//&#x5B50;&#x7EC4;&#x4EF6;
function RadioOption(props) {
  return (
    <label>
      <input type="radio" value="{props.value}" name="{props.name}">
      {props.label}
    </label>
  )
}

然后是父组件,不仅需要把它所有的子组件显示出来,还需要为每个子组件赋上name属性和值:

//&#x7236;&#x7EC4;&#x4EF6;&#x7528;,props&#x662F;&#x6307;&#x7236;&#x7EC4;&#x4EF6;&#x7684;props
function renderChildren(props) {

  //&#x904D;&#x5386;&#x6240;&#x6709;&#x5B50;&#x7EC4;&#x4EF6;
  return React.Children.map(props.children, child => {
    if (child.type === RadioOption)
      return React.cloneElement(child, {
        //&#x628A;&#x7236;&#x7EC4;&#x4EF6;&#x7684;props.name&#x8D4B;&#x503C;&#x7ED9;&#x6BCF;&#x4E2A;&#x5B50;&#x7EC4;&#x4EF6;
        name: props.name
      })
    else
      return child
  })
}

//&#x7236;&#x7EC4;&#x4EF6;
function RadioGroup(props) {
  return (
    <div>
      {renderChildren(props)}
    </div>
  )
}

function App() {
  return (
    <radiogroup name="hello">
      <radiooption label="&#x9009;&#x9879;&#x4E00;" value="1">
      <radiooption label="&#x9009;&#x9879;&#x4E8C;" value="2">
      <radiooption label="&#x9009;&#x9879;&#x4E09;" value="3">
    </radiooption></radiooption></radiooption></radiogroup>
  )
}

export default App;

以上, React.Children.map让我们对父组件的所有子组件又更灵活的控制。

Original: https://www.cnblogs.com/darrenji/p/5650410.html
Author: Darren Ji
Title: React中props.children和React.Children的区别

相关阅读3

Title: 单细胞亚群降维聚类分群

单细胞亚群降维聚类分群

大家好,这里是想做生信大恐龙🦖的生信小白,今天的暗杀目标是一个叫做'降维聚类分析'的小boss,看完记得一键三连(点赞收藏+评论)。文章参考微信公众号"生信技能树",也学生信的小伙伴可以关注哦!

降维聚类分析

前言

在单细胞和多细胞生物中,单个细胞之间的差异可以产生较大的功能影响。单细胞RNA测序方法揭示了组织组成、转录动态和基因之间的调控关系方面的新的生物学。不但如此,单细胞RNA测序可以指导细胞分类,更精确地确定细胞种类。

一、数据准备

1.打开 https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE173087网站。
基于docker的tensorflow2 bert 新闻分类模型部署
2. 下载17.4Mb的http文件,保存至一个没有中文名称的路径。

; 二、实现分类(过程记录)

1.引入相关库

引入相关库代码如下:

library(Seurat)
library(data.table)

如果没有下载这些包的小伙伴,可以使用下列代码下载相关的R包:

install.packages('Seurat')
install.packages('data.frame')

下载好相关包之后,运行出现了版本不兼容的警告。
基于docker的tensorflow2 bert 新闻分类模型部署
使用下列代码更新R语言版本:

install.package('installr')
library(installr)
updateR()

更新之后,紧接着出现了报错了
基于docker的tensorflow2 bert 新闻分类模型部署
大概的意思是要我们使用install.packages('R.utils')安装'R.utils'。按照它的要求进行。运行下列代码即可进行:

install.package('R.utils')

基于docker的tensorflow2 bert 新闻分类模型部署

2.数据处理

接下来对数据进行处理,运行下列代码

library(Seurat)
library(data.table)

ct=fread( 'C:/Users/86184/Desktop/exp/bioinf_student/single_data/GSE173087_Maize_rootsc_counts.txt.gz',data.table = F)
ct[1:4,1:4]
rownames(ct)=ct[,1]
ct=ct[,-1]

maize  CreateSeuratObject(counts = ct,
                            project = "maize",
                            min.cells = 3,
                            min.features = 200)
maize

maize  NormalizeData(maize, normalization.method = "LogNormalize",
                       scale.factor = 10000)

maize  NormalizeData(maize)

maize  FindVariableFeatures(maize, selection.method = "vst", nfeatures = 2000)

all.genes  rownames(maize)
maize  ScaleData(maize, features = all.genes)

maize  RunPCA(maize, features = VariableFeatures(object = maize),
                verbose = FALSE)
maize  FindNeighbors(maize, dims = 1:10, verbose = FALSE)
maize  FindClusters(maize, resolution = 0.5, verbose = FALSE)
maize  RunUMAP(maize, dims = 1:10, umap.method = "uwot", metric = "cosine")
table(maize$seurat_clusters)
phe=maize@meta.data
save(phe,file = 'phe-by-basic-seurat.Rdata')

DimPlot(maize, reduction = "umap", group.by = 'seurat_clusters',
        label = TRUE, pt.size = 0.5)

结果如下图:

基于docker的tensorflow2 bert 新闻分类模型部署
可以看到2000个单细胞被分成了十八类。虽然对于Seurat原理我还不是很理解,但是对于R中的这个库的使用还是理解了一点。

总结

以上就是本次的内容,本文记录了学习"生信技能树"公众号的学徒任务中的单细胞降维聚类分群,大家有相关的想法可以评论交流,尤其是关于生物上的和语言上的,🦖都很感兴趣的。最后看到这里的小伙伴不妨一键三连啊(点赞评论+关注)

Original: https://blog.csdn.net/ouyangk1026/article/details/122135105
Author: Bio大恐龙
Title: 单细胞亚群降维聚类分群