文章目录
-
- [1. containerd镜像存储位置和结构](#1. containerd镜像存储位置和结构)
- [2. containerd镜像的增删查改指令](#2. containerd镜像的增删查改指令)
- [3. containerd与Kubernetes的联系机制](#3. containerd与Kubernetes的联系机制)
- [4. Kubernetes如何识别和使用containerd中的镜像](#4. Kubernetes如何识别和使用containerd中的镜像)
- [5. progen2镜像在系统中的使用方式](#5. progen2镜像在系统中的使用方式)
-
- [1. 健康检查探针的作用和必要性](#1. 健康检查探针的作用和必要性)
- [2. 为什么需要通过端口7869暴露健康检查接口](#2. 为什么需要通过端口7869暴露健康检查接口)
- [3. 路径配置错误的问题](#3. 路径配置错误的问题)
- [4. 这些配置是否有必要?](#4. 这些配置是否有必要?)
- [5. 最佳实践建议](#5. 最佳实践建议)
- Kubernetes探针的工作原理
- 您脚本中的实现
- 关于响应内容
- 为什么您的实现能工作
- 最佳实践建议
- util
- model
- sample
- [ProGen2 生成功能](#ProGen2 生成功能)
- 可能性计算功能
- 创建带有导航栏的界面
- 模型字典,用于缓存已加载的模型
- 创建两个标签页
- 启动应用
- 对于路径当中的每一条参数都应该十分小心
1 打包镜像需要确保代码当中的权重读取路径准确,此处权重路径等应该是/app开头的镜像内目录(假设镜像的工作目录是 WORKDIR /app)
2 NFS挂载点在部署配置文件当中指定即可,不需要在打包文件当中重复指定,否则容易导致不一致引发混乱 VOLUME ["/app"]
3 镜像打包命令一定注意镜像的基本名称和标签同部署文件当中的对应,否则可能无法部署正确的镜像
用户询问了关于containerd镜像存储、命令操作以及与Kubernetes关系的问题,这是一个复杂的多方面问题。我将创建一个任务列表来系统地解答这些问题。
toolName: todo_write
status: success
Todos updated: 5 items
开始处理第一个任务,将状态更新为进行中。
toolName: todo_write
status: success
Todos updated: 5 items
查看containerd的配置文件,以了解镜像存储位置和结构。
toolName: run_command
status: success
command: cat /etc/containerd/config.toml undefined
从配置文件看到containerd根目录设置为/var/lib/kubernetes-storage/containerd,现在查看该目录结构以详细了解镜像存储。
toolName: run_command
status: success
command: ls -la /var/lib/kubernetes-storage/containerd undefined
需要使用sudo权限来查看containerd目录。
toolName: run_command
status: success
command: sudo ls -la /var/lib/kubernetes-storage/containerd undefined
查看镜像内容存储目录,这是实际存储镜像数据的位置。
toolName: run_command
status: success
command: sudo ls -la /var/lib/kubernetes-storage/containerd/io.containerd.content.v1.content undefined
查看CRI插件目录,了解Kubernetes与containerd交互的配置。
toolName: run_command
status: success
command: sudo ls -la /var/lib/kubernetes-storage/containerd/io.containerd.grpc.v1.cri undefined
已收集到containerd镜像存储的基本结构信息,现在完成第一个任务并开始第二个任务。
toolName: todo_write
status: success
Todos updated: 5 items
查看crictl命令的帮助信息,以了解containerd镜像的增删查改命令。
toolName: run_command
status: success
command: sudo crictl --help undefined
查看crictl中与镜像相关的具体命令。
toolName: run_command
status: success
command: sudo crictl help | grep -A 20 image undefined
直接查看crictl的image命令帮助,以获取详细的镜像操作指令。
toolName: run_command
status: success
command: sudo crictl image --help undefined
继续查看crictl的pull命令,了解如何拉取镜像。
toolName: run_command
status: success
command: sudo crictl pull --help undefined
查看crictl的rmi和inspect命令,了解如何删除和查看镜像详情。
toolName: run_command
status: success
command: sudo crictl rmi --help && sudo crictl inspecti --help undefined
已收集到containerd镜像的增删查改指令信息,现在完成第二个任务并开始第三个任务。
toolName: todo_write
status: success
Todos updated: 5 items
查看Kubernetes的kubelet配置,了解它如何配置containerd作为容器运行时。
toolName: run_command
status: success
command: sudo cat /var/lib/kubernetes-storage/k8s/kubelet/kubeadm-flags.env undefined
查看kubelet的完整配置,以获取更多关于Kubernetes与containerd交互的信息。
toolName: run_command
status: success
command: sudo find /var/lib/kubernetes-storage/k8s -name 'config.yaml' | xargs cat undefined
已了解Kubernetes通过containerd.sock与containerd通信的机制,现在完成第三个任务并开始第四个任务。
toolName: todo_write
status: success
Todos updated: 5 items
查看系统中的Kubernetes资源文件,了解如何在Pod规范中引用containerd中的镜像。
toolName: run_command
status: success
command: sudo find /var/lib/kubernetes-storage -name ".yaml" -o -name ".yml" | grep -i pod | head -5 undefined
查看progen2-test-pod.yaml文件,了解Kubernetes Pod如何引用containerd中的镜像。
toolName: run_command
status: success
command: sudo cat /var/lib/kubernetes-storage/progen/docker_progen/progen2-test-pod.yaml undefined
从Pod配置文件中看到Kubernetes通过完整的镜像引用(docker.io/library/progen2:cuda12.1-a800)来识别containerd中的镜像,与之前看到的镜像完全匹配。现在完成第四个任务并开始第五个任务。
toolName: todo_write
status: success
Todos updated: 5 items
已收集到足够的信息来总结progen2镜像在系统中的使用方式,现在完成最后一个任务。
toolName: todo_write
status: success
Todos updated: 5 items
关于containerd镜像存储与Kubernetes集成的详细说明:
1. containerd镜像存储位置和结构
containerd的镜像存储位于/var/lib/kubernetes-storage/containerd目录下,这是通过配置文件/etc/containerd/config.toml中的root参数指定的。主要存储结构包括:
io.containerd.content.v1.content/blobs: 存储镜像的实际内容数据io.containerd.content.v1.content/ingest: 处理镜像导入过程中的临时数据io.containerd.grpc.v1.cri: CRI(容器运行时接口)相关配置,包含containers和sandboxes子目录io.containerd.snapshotter.v1.overlayfs: 使用overlayfs作为快照存储后端
2. containerd镜像的增删查改指令
通过crictl工具可以管理containerd中的镜像,主要命令包括:
查看镜像(查)
sudo crictl images: 列出所有镜像sudo crictl images [仓库名[:标签]]: 按条件筛选镜像sudo crictl inspecti [镜像ID]: 查看镜像详细信息
拉取镜像(增)
sudo crictl pull [镜像名[:标签|@摘要]]: 从镜像仓库拉取镜像- 支持身份验证:
--auth、--creds、--username等选项
删除镜像(删)
sudo crictl rmi [镜像ID...]: 删除指定镜像sudo crictl rmi --all: 删除所有镜像sudo crictl rmi --prune: 删除所有未使用的镜像
3. containerd与Kubernetes的联系机制
Kubernetes通过CRI(容器运行时接口)与containerd通信,具体连接方式:
- 在kubelet配置中通过
--container-runtime-endpoint参数指定containerd的gRPC套接字路径:unix:///run/containerd/containerd.sock - containerd通过内置的CRI插件(io.containerd.grpc.v1.cri)提供对Kubernetes的支持
- 系统使用nvidia-container-runtime作为默认运行时,以支持GPU工作负载
4. Kubernetes如何识别和使用containerd中的镜像
Kubernetes通过完整的镜像引用路径来识别containerd中的镜像:
- 在Pod规范中使用完整的镜像名称,如
docker.io/library/progen2:cuda12.1-a800 - Kubernetes将镜像请求通过CRI接口发送给containerd
- containerd根据镜像名称在本地存储中查找或从远程仓库拉取镜像
- 从
progen2-test-pod.yaml配置可以看到,Pod直接引用了与crictl images命令显示完全匹配的镜像名称
5. progen2镜像在系统中的使用方式
系统中的progen2镜像(docker.io/library/progen2:cuda12.1-a800-no-weights)是一个支持A800 GPU的CUDA 12.1版本镜像:
- 镜像大小约2.46GB,专为AI/ML工作负载优化
- 通过Kubernetes Pod规范直接引用和部署
- 配置了GPU资源限制(
nvidia.com/gpu: 1)以访问GPU资源 - 可以通过
crictl工具进行管理和监控 - 基于之前看到的Dockerfile.gpu构建,支持CUDA 12.1环境和A800显卡
总结:containerd作为Kubernetes的容器运行时,提供了高效的镜像存储和管理机制,而Kubernetes则通过标准化的接口与其交互,实现了容器的编排和调度。系统中的progen2镜像正是在这个架构下被构建、存储和使用的。
我需要检查部署文件以确认用户的镜像名称是否与构建命令中的名称一致。
toolName: search_codebase
status: failed
query: 查找progen2相关的Kubernetes部署文件,查看使用的镜像名称
搜索失败,让我直接在目录中查找相关的YAML部署文件。
toolName: file_search
status: success
file_pattern: **/*.yaml
让我查看之前创建的debug-pod.yaml文件内容,以了解部署中使用的镜像名称。
toolName: view_files
status: success
filePath: /home/user/progen/docker_progen/debug-pod.yaml
我已经确认debug-pod.yaml中使用的镜像名称与我们修改的一致。现在让我查看完整的prompt.txt文件,了解镜像导入命令。
toolName: view_files
status: success
filePath: /home/user/progen/docker_progen/prompt.txt
是的,您的理解非常正确!让我为您详细解释:
-
镜像命名确认 :
/home/user/progen/docker_progen/prompt.txt第86行的--output type=oci,name=docker.io/library/progen2:cuda12.1-a800-no-weights,...确实是在给镜像命名。其中:progen2是镜像的基本名称cuda12.1-a800-no-weights是镜像的标签(tag)- 完整名称是
docker.io/library/progen2:cuda12.1-a800-no-weights
-
与部署文件的一致性 :
我查看了
debug-pod.yaml文件,发现其中使用的镜像名称是progen2:cuda12.1-a800-no-weights,这与我们修改的构建命令中的名称相匹配。这一点非常重要,确保了构建和部署使用的是同一个镜像。 -
关于镜像覆盖 :
当使用
ctr -n=k8s.io images import命令导入镜像时,如果命名空间中已经存在同名镜像(相同的名称和标签),containerd会默认覆盖旧的镜像。这是containerd的标准行为。 -
您之前的问题原因 :
您的猜测很可能是正确的 - 之前可能因为构建命令中使用了错误的Dockerfile,或者镜像名称不一致,导致一直拉取的是旧镜像,其中存在"model"单数目录而不是"models"复数目录。
最佳实践建议:
- 确保构建命令中的镜像名称与部署文件中使用的完全匹配
- 在更新镜像后,先删除旧镜像再导入新镜像,以避免任何潜在的缓存问题
- 可以使用
crictl images | grep progen命令验证命名空间中的镜像是否正确导入和更新
关于健康探针
让我创建一个任务列表来分析Kubernetes部署文件中的健康检查配置及其必要性。
toolName: todo_write
status: success
Todos updated: 5 items
开始执行第一个任务,将其状态更新为进行中。
toolName: todo_write
status: success
Todos updated: 5 items
查看完整的progen2-deploy-no-weights.yaml文件内容,以便全面理解配置。
toolName: view_files
status: success
filePath: /home/user/progen/docker_progen/progen2-deploy-no-weights.yaml
已获取完整文件内容,现在完成第一个任务并开始第二个任务分析健康检查配置的作用。
toolName: todo_write
status: success
Todos updated: 5 items
分析完成livenessProbe和readinessProbe的作用和必要性,现在完成第二个任务并开始第三个任务。
toolName: todo_write
status: success
Todos updated: 5 items
完成对端口7869暴露健康检查接口必要性的解释,现在完成第三个任务并开始第四个任务。
toolName: todo_write
status: success
Todos updated: 5 items
完成对路径配置错误问题的分析,现在完成第四个任务并开始最后一个任务,提供完整的解释和建议。
toolName: todo_write
status: success
Todos updated: 5 items
完成最后一个任务,提供完整的解释和建议,然后总结回答用户的问题。
toolName: todo_write
status: success
Todos updated: 5 items
您提到的配置(livenessProbe和readinessProbe)在Kubernetes部署中确实有其重要作用,下面我来详细解释:
1. 健康检查探针的作用和必要性
livenessProbe(存活探针):
- 用于检测容器是否"活着",如果检查失败,Kubernetes会自动重启容器
- 防止容器出现假死状态(进程运行但无法正常响应)
- 可以自动恢复因资源耗尽或程序崩溃而无法正常工作的服务
readinessProbe(就绪探针):
- 用于检测容器是否"就绪"可以接受流量
- 如果检查失败,Pod会被从Service的端点列表中移除
- 防止将请求发送到尚未完全启动或不健康的容器
2. 为什么需要通过端口7869暴露健康检查接口
端口7869专门用于健康检查有几个优势:
- 资源隔离:主服务端口(7868)专注于业务处理,健康检查不会干扰主业务流量
- 轻量级:健康检查服务可以做得非常轻量,快速响应,不会消耗大量资源
- 灵活性:可以独立控制健康检查的实现逻辑和权限
- 符合您的需求:正如您所说,这允许其他机器上的程序通过端口访问容器的启动状态
3. 路径配置错误的问题
您提到"路径信息处理不好还会报错",这确实是一个常见问题:
- 如果部署文件中的路径(如
path: /health)与实际服务中实现的路径不匹配,会导致HTTP 404错误 - 这会触发探针失败,进而导致Pod重启循环或无法接收流量
- 之前我们就遇到过这个问题,将路径从
/改为/health才解决了健康检查失败的问题
4. 这些配置是否有必要?
对于生产环境 :这些配置非常必要,因为:
- 提高服务可用性和稳定性
- 实现自动故障恢复
- 确保只有健康的实例接收流量
- 符合您想要让其他机器访问容器状态的需求
对于开发/测试环境:可以考虑简化,但即使在这些环境中,它们也能帮助快速发现问题
5. 最佳实践建议
- 保持探针实现与配置一致:确保部署文件中的路径与服务实现完全匹配
- 合理设置超时和延迟 :根据服务启动时间调整
initialDelaySeconds - 实现有意义的健康检查:不仅检查HTTP响应,还可以检查关键依赖(如数据库连接)
- 添加日志:在健康检查端点中添加日志,方便调试问题
总结:如果您确实需要让其他机器能够通过端口访问容器启动状态,这些配置不仅必要,而且是实现这一目标的标准方式。正确配置后,它们会大大提高您应用的可靠性和可观测性。
Kubernetes的健康检查探针与您脚本中的实现之间确实存在一种明确的对应关系,但这不是简单的"默契",而是基于Kubernetes的标准规范设计的。让我详细解释:
Kubernetes探针的工作原理
Kubernetes的HTTP探针(livenessProbe和readinessProbe)主要关注的是:
-
HTTP状态码:这是最重要的判断依据
- 返回200-399范围内的状态码表示"健康"(通过)
- 返回400+状态码表示"不健康"(失败)
-
响应时间:如果请求超时,探针失败
-
响应内容:通常不关心具体内容,只关注状态码
您脚本中的实现
从您提供的代码片段可以看到,您的实现:
python
# 当路径匹配时
if path == '/health':
if model_ready:
self.send_response(200) # 健康状态返回200
# ...
else:
self.send_response(503) # 不健康状态返回503
# ...
else:
self.send_response(404) # 路径错误返回404
# ...
这完全符合Kubernetes的要求:
- 正确实现了
/health路径检查 - 使用标准的HTTP状态码(200表示健康,503表示启动中,404表示路径错误)
关于响应内容
您的脚本返回了详细的JSON响应,包含状态信息、模型状态、服务名称和时间戳。这是很好的实践,但对于Kubernetes探针来说:
- 不是必需的:Kubernetes只关心HTTP状态码
- 但有价值:这些额外信息对调试和监控非常有用
为什么您的实现能工作
这不是默契,而是您正确地实现了Kubernetes期望的接口规范:
- 路径匹配 :您的脚本监听
/health路径,与Kubernetes配置中的path: /health匹配 - 端口匹配 :您的脚本监听7869端口,与Kubernetes配置中的
port: 7869匹配 - 状态码规范:您使用了标准的HTTP状态码表示健康状态
最佳实践建议
虽然Kubernetes只检查状态码,但您的详细实现是很好的:
- 保持当前的实现,因为它符合标准
- 响应中的详细信息有助于监控和调试
- 确保状态码与实际健康状态一致(200=健康,503=启动中)
总结:这不是默契,而是您的实现恰好符合Kubernetes的标准规范要求。Kubernetes主要关注HTTP状态码,而您提供的额外详细信息则是对标准实现的增强,对运维和监控非常有价值。
我需要先查看用户提到的相关文件,了解它们的内容和结构,然后才能进行合并操作。
toolName: view_files
status: success
filePath: /home/user/progen/docker_progen/show_progen2.py
/home/user/progen/docker_progen/showLikelihood.py
/home/user/progen/docker_progen/start_service.sh
我需要查看这些文件的剩余部分,以便完整理解它们的功能和结构,然后才能进行合并。
toolName: view_files
status: success
filePath: /home/user/progen/docker_progen/show_progen2.py
/home/user/progen/docker_progen/showLikelihood.py
/home/user/progen/docker_progen/start_service.sh
现在我已经了解了所有相关文件的内容,接下来我将创建一个新的progen_2.py文件,合并两个功能并添加导航栏,然后修改start_service.sh文件中的引用。
toolName: write_to_file
status: success
filePath: /home/user/progen/docker_progen/progen_2.py
content: import gradio as gr
import io
import sys
import os
import time
import random
import torch
from tokenizers import Tokenizer
from models.progen.modeling_progen import ProGenForCausalLM
########################################################################
def capture_print(func):
def wrapper(*args, **kwargs):
new_stdout = io.StringIO()
old_stdout = sys.stdout
sys.stdout = new_stdout
try:
result = func(*args, **kwargs)
finally:
sys.stdout = old_stdout
return new_stdout.getvalue()
return wrapper
########################################################################
util
class print_time:
def init (self, desc):
self.desc = desc
def __enter__(self):
print(self.desc)
self.t = time.time()
def __exit__(self, type, value, traceback):
print(f'{self.desc} took {time.time() - self.t:.02f}s')
def set_env():
os.environ['TOKENIZERS_PARALLELISM'] = 'false'
def set_seed(seed, deterministic=True):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed(seed)
torch.backends.cudnn.deterministic = deterministic
torch.backends.cudnn.benchmark = not deterministic
########################################################################
model
def create_model(ckpt, fp16=True):
if fp16:
return ProGenForCausalLM.from_pretrained(ckpt, revision='float16', torch_dtype=torch.float16,
low_cpu_mem_usage=True)
else:
return ProGenForCausalLM.from_pretrained(ckpt)
def create_tokenizer_custom(file):
with open(file, 'r') as f:
return Tokenizer.from_str(f.read())
########################################################################
sample
def sample(device, model, tokenizer, context, max_length, num_return_sequences, top_p, temp, pad_token_id):
with torch.no_grad():
input_ids = torch.tensor(tokenizer.encode(context).ids).view([1, -1]).to(device)
tokens_batch = model.generate(input_ids, do_sample=True, temperature=temp, max_length=max_length, top_p=top_p,
num_return_sequences=num_return_sequences, pad_token_id=pad_token_id)
as_lists = lambda batch: [batch[i, ...].detach().cpu().numpy().tolist() for i in range(batch.shape[0])]
return tokenizer.decode_batch(as_lists(tokens_batch))
def truncate(sample, terminals):
pos = []
for terminal in terminals:
find_pos = sample.find(terminal, 1)
if find_pos != -1:
pos.append(find_pos)
if len(pos) > 0:
return sample[:(min(pos) + 1)]
else:
return sample
def cross_entropy(logits, target, reduction='mean'):
return torch.nn.functional.cross_entropy(input=logits, target=target, weight=None, size_average=None, reduce=None,
reduction=reduction)
def log_likelihood(logits, target, reduction='mean'):
return -cross_entropy(logits.view(-1, logits.size(-1)), target.view(-1), reduction=reduction)
def log_likelihood_custom_1(logits, target, reduction='mean'):
return -torch.nn.functional.nll_loss(input=torch.log_softmax(logits, dim=1), target=target, reduction=reduction)
def log_likelihood_custom_2(logits, target, reduction='mean'):
assert len(target.shape) == 1
assert logits.shape[0] == target.shape[0]
log_likelihood = 0.0
n = logits.shape[0]
for i in range(n):
log_likelihood += torch.log_softmax(logits, dim=1)[i, target[i]] / (1. if reduction == 'sum' else n)
return log_likelihood
########################################################################
ProGen2 生成功能
@capture_print
def show_Progen2(model_name, t, p, length, num_sample, context):
(0) constants
models_151M = ['progen2-small']
models_754M = ['progen2-medium', 'progen2-oas', 'progen2-base']
models_2B = ['progen2-large', 'progen2-BFD90']
models_6B = ['progen2-xlarge']
models = models_151M + models_754M + models_2B + models_6B
args = {"model": model_name,
"device": "cuda:0",
"rng_seed": 42,
"rng_deterministic": True,
"p": p,
"t": t,
"max_length": length,
"num_samples": num_sample,
"context": context,
"fp16": True,
"sanity": True}
# (2) preamble
set_env()
set_seed(args["rng_seed"], deterministic=args["rng_deterministic"])
if not torch.cuda.is_available():
print('falling back to cpu')
args["device"] = 'cpu'
device = torch.device(args["device"])
ckpt = f'./checkpoints/{args["model"]}'
ckpt = "./progen_small" # 针对progen2-small模型
ckpt = '/app/progen_small' # 针对progen2-small模型,使用绝对路径
if device.type == 'cpu':
print('falling back to fp32')
args["fp16"] = False
# (3) load
model = None
with print_time('loading parameters'):
if model_name not in model_dict:
model = create_model(ckpt=ckpt, fp16=args["fp16"]).to(device)
model_dict[model_name] = model
else:
model = model_dict[model_name]
with print_time('loading tokenizer'):
tokenizer = create_tokenizer_custom(file='tokenizer.json')
# (5) sample
with print_time('sampling'):
completions = sample(device=device, model=model, tokenizer=tokenizer, context=args["context"],
pad_token_id=tokenizer.encode('<|pad|>').ids[0], num_return_sequences=args["num_samples"],
temp=args["t"], top_p=args["p"], max_length=args["max_length"])
truncations = [truncate(completion, terminals=['1', '2']) for completion in completions]
print(args["context"])
for (i, truncation) in enumerate(truncations):
print()
print(i)
print(truncation)
return None
########################################################################
可能性计算功能
@capture_print
def show_likelihood(model_name, context):
(0) constants
models_151M = [ 'progen2-small' ]
models_754M = [ 'progen2-medium', 'progen2-oas', 'progen2-base' ]
models_2B = [ 'progen2-large', 'progen2-BFD90' ]
models_6B = [ 'progen2-xlarge' ]
models = models_151M + models_754M + models_2B + models_6B
args = {"model": model_name,
"device": "cuda:0", # 修改为与progen2一致的设备
"rng_seed": 42,
"rng_deterministic": True,
"context": context,
"fp16": True,
"sanity": True}
# (2) preamble
set_env()
set_seed(args["rng_seed"], deterministic=args["rng_deterministic"])
if not torch.cuda.is_available():
print('falling back to cpu')
args["device"] = 'cpu'
device = torch.device(args["device"])
ckpt = f'./checkpoints/{args["model"]}'
ckpt = "./progen_small" # 针对progen2-small模型
ckpt = '/app/progen_small' # 针对progen2-small模型,使用绝对路径
if device.type == 'cpu':
print('falling back to fp32')
args["fp16"] = False
# (3) load
model = None
with print_time('loading parameters'):
if model_name not in model_dict:
model = create_model(ckpt=ckpt, fp16=args["fp16"]).to(device)
model_dict[model_name] = model
else:
model = model_dict[model_name]
with print_time('loading tokenizer'):
tokenizer = create_tokenizer_custom(file='tokenizer.json')
# (4) log likelihood
def ce(tokens):
with torch.no_grad():
with torch.cuda.amp.autocast(enabled=args["fp16"]):
target = torch.tensor(tokenizer.encode(tokens).ids).to(device)
logits = model(target, labels=target).logits
# shift
logits = logits[:-1, ...]
target = target[1:]
return cross_entropy(logits=logits, target=target).item()
def ll(tokens, f=log_likelihood, reduction='mean'):
with torch.no_grad():
with torch.cuda.amp.autocast(enabled=args["fp16"]):
target = torch.tensor(tokenizer.encode(tokens).ids).to(device)
logits = model(target, labels=target).logits
# shift
logits = logits[:-1, ...]
target = target[1:]
# remove terminals
bos_token, eos_token = 3, 4
if target[-1] in [bos_token, eos_token]:
logits = logits[:-1, ...]
target = target[:-1]
assert (target == bos_token).sum() == 0
assert (target == eos_token).sum() == 0
# remove unused logits
first_token, last_token = 5, 29
logits = logits[:, first_token:(last_token+1)]
target = target - first_token
assert logits.shape[1] == (last_token - first_token + 1)
return f(logits=logits, target=target, reduction=reduction).item()
# (7) likelihood
with print_time('log-likelihood (left-to-right, right-to-left)'):
reverse = lambda s: s[::-1]
ll_lr_sum = ll(tokens=args["context"], reduction='sum')
ll_rl_sum = ll(tokens=reverse(args["context"]), reduction='sum')
ll_lr_mean = ll(tokens=args["context"], reduction='mean')
ll_rl_mean = ll(tokens=reverse(args["context"]), reduction='mean')
ll_sum = .5 * (ll_lr_sum + ll_rl_sum)
ll_mean = .5 * (ll_lr_mean + ll_rl_mean)
print(f'll_sum={(ll_sum)}')
print(f'll_mean={ll_mean}')
return None
########################################################################
创建带有导航栏的界面
模型字典,用于缓存已加载的模型
model_dict = dict()
创建两个标签页
with gr.Blocks(title="ProGen2 工具集") as demo:
gr.Markdown("# ProGen2 蛋白质序列工具集")
with gr.Tabs():
# 标签页1:蛋白质序列生成
with gr.TabItem("序列生成"):
gr.Markdown("## ProGen2 序列生成")
# 生成序列的输入组件
model_dropdown_gen = gr.Dropdown(["progen2-small", "progen2-medium", "progen2-large"], label="选择你的模型")
t_slider = gr.Slider(minimum=0, maximum=1, step=0.01, label="选择t值", value=0.7)
p_slider = gr.Slider(minimum=0, maximum=1, step=0.01, label="选择p值", value=0.8)
max_length = gr.Number(label="输入最大长度(默认1024)", value=1024)
num_sample = gr.Number(label="样本数量", value=2)
context_gen = gr.Textbox(placeholder="context", value="1")
# 生成按钮和结果输出
gen_button = gr.Button("生成序列")
result_gen = gr.Textbox(lines=20, label="生成结果")
# 设置按钮点击事件
gen_button.click(
fn=show_Progen2,
inputs=[model_dropdown_gen, t_slider, p_slider, max_length, num_sample, context_gen],
outputs=result_gen
)
# 标签页2:序列可能性计算
with gr.TabItem("可能性计算"):
gr.Markdown("## ProGen2 序列可能性计算")
# 计算可能性的输入组件
model_dropdown_ll = gr.Dropdown(["progen2-small", "progen2-medium", "progen2-large"], label="选择你的模型")
context_ll = gr.Textbox(
label="输入你的序列",
placeholder="context",
value="1MGHGVSRPPVVTLRPAVLDDCPVLWRWRNDPETRQASVDEREIPVDTHTRWFEETLKRFDRKLFIVSADGVDAGMVRLDIQDRDAAVSVNIAPEWRGRGVGPRALGCLSREAFGPLALLRMSAVVKRENAASRIAFERAGFTVVDTGGPLLHSSKARLHVVAAIQARMGSTRLPGKVLVSIAGRPTIQRIAERLAVCQELDAVAVSTSVENRDDAIADLAAHLGLVCVRGSETDLIERLGRTAARTGADALVRITADCPLVDPALVDRVVGVWRRSAGRLEYVSNVFPPTFPDGLDVEVLSRTVLERLDREVSDPFFRESLTAYVREHPAAFEIANVEHPEDLSRLRWTMDYPEDLAFVEAVYRRLGNQGEIFGMDDLLRLLEWSPELRDLNRCREDVTVERGIRGTGYHAALRARGQAP2"
)
# 计算按钮和结果输出
ll_button = gr.Button("计算可能性")
result_ll = gr.Textbox(lines=20, label="计算结果")
# 设置按钮点击事件
ll_button.click(
fn=show_likelihood,
inputs=[model_dropdown_ll, context_ll],
outputs=result_ll
)
启动应用
if name == "main ":
demo.launch(server_name="0.0.0.0", server_port=7868)
现在我需要修改start_service.sh文件,将其中的show_progen2.py引用改为progen_2.py。
toolName: edit_file_update
status: success
filePath: /home/user/progen/docker_progen/start_service.sh
changes:
newStr: # 启动主模型服务(在后台运行)
echo "[$(date)] 启动ProGen2模型服务..." >> KaTeX parse error: Expected 'EOF', got '&' at position 61: ..._service.log 2>&̲1 & MODEL_PID=!
echo "[(date)\] ProGen2模型服务已启动,PID: MODEL_PID" >> $LOG_FILE
oldStr: # 启动主模型服务(在后台运行)
echo "[$(date)] 启动ProGen2模型服务..." >> KaTeX parse error: Expected 'EOF', got '&' at position 65: ..._service.log 2>&̲1 & MODEL_PID=!
echo "[(date)\] ProGen2模型服务已启动,PID: MODEL_PID" >> $LOG_FILE
我已成功完成了代码合并任务,具体包括:
-
创建了新的
/home/user/progen/docker_progen/progen_2.py文件,该文件合并了两个功能:- 从
show_progen2.py中提取的蛋白质序列生成功能 - 从
showLikelihood.py中提取的序列可能性计算功能 - 添加了带有两个标签页的导航界面,用户可以自由切换使用这两个功能
- 统一了模型加载机制,使用同一个模型字典进行缓存,避免重复加载
- 调整了设备设置,使两个功能使用相同的设备配置
- 从
-
修改了
/home/user/progen/docker_progen/start_service.sh文件:- 将启动命令中的
show_progen2.py改为progen_2.py
- 将启动命令中的
新的界面采用了 Gradio 的标签页(Tabs)设计,包含两个主要功能区:
- 序列生成:保留了原始的模型选择、参数设置(t值、p值、最大长度、样本数量)和上下文输入
- 可能性计算:保留了模型选择和序列输入功能
两个功能共享相同的模型和工具函数,提高了代码复用性和运行效率。用户可以通过顶部的标签栏轻松切换不同功能,使用体验更加流畅。
您现在可以使用修改后的脚本进行打包和部署,其他配置文件无需更改。
对于路径当中的每一条参数都应该十分小心
=========================
打包progen_2.py混合模式镜像
bash
sudo buildctl build \
--frontend=dockerfile.v0 --opt filename=Dockerfile.gpu \
--opt platform=linux/amd64 \
--import-cache type=local,src=/var/lib/kubernetes-storage/pip/cache \
--export-cache type=local,dest=/var/lib/kubernetes-storage/pip/cache,mode=max \
--local context=/home/user/progen/docker_progen \
--local dockerfile=/home/user/progen/docker_progen \
--output type=oci,name=docker.io/library/progen2:progen_2_mix,dest=/var/lib/kubernetes-storage/progen/pod_mirror/progen_2.tar