当前位置: 首页 > article >正文

单细胞组学大模型(7)--- GenePT,一个可以在本地部署和使用的单细胞转录组大模型


–https://doi.org/10.1038/s41551-024-01284-6

代码来源:https://github.com/yiqunchen/GenePT

Simple and effective embedding model for single-cell biology built from ChatGPT

研究作者和单位

James Zou–Department of Biomedical Data Science, Stanford University

Yiqun Chen–Department of Biomedical Data Science, Stanford University

目录:

  • 1.研究简介
  • 2.基因文本获取以及转成embedding
  • 3.模型架构
  • 4.研究结果展示

1.研究简介

单细胞测序领域正在兴起一股开发“基础模型”的热潮,目的是学习基因和细胞的embedding表示,促进各种下游分析。

1.1 现有方法(scBERT、Geneformer、scGPT)

采用深度学习架构,通常是基于 Transformer。

预训练: 使用大规模单细胞基因表达数据集,以自监督方式(例如,通过填补masked的表达值)进行预训练。训练后的编码器将基因和细胞映射到高维embedding向量,其中包含潜在的生物学信息。

微调: 对于下游任务,可以选择使用少量特定于任务的数据进行微调,以提高预测能力。

以下是之前我发布的相关推文:

1.2 局限性:

  1. 仅从基因表达数据集生成embedding表示,没有利用关于基因的文献和现有知识。

  2. 大规模单细胞转录组学数据的收集、处理和训练需要大量工作。

  3. 提取的c表示信号很大程度上依赖于使用的基因表达数据,没有利用总结基因功能的庞大研究和文献,可能导致某些应用中样本效率低下和次优结果。

1.3 GenePT

利用 OpenAI 的 ChatGPT 文本嵌入模型来表示基因和细胞,GenePT把文本知识embedding和单细胞转录组数据embedding结合,研究利用自然语言对基因和细胞的生物学进行编码的可行性,弥补现有方法在利用现有生物学知识方面的不足。

因为大型语言模型已经利用大量计算资源在广泛的文本语料库(包括生物医学文献)上进行训练,并且在理解、推理甚至生成生物医学文本方面表现出卓越的能力。

因此,作者假设 LLM 衍生的基因summary和功能embedding表示(通常是从广泛的实验和研究中策划出来的)可以更直接地捕获底层的生物学信息。

GenePT 在各种下游任务上的表现可与 Geneformer 等专门设计的模型相当,有时甚至超越它们:

(i)它在生物学任务上表现的更好;

(ii)它不需要对基因组数据进行大量数据集整理处理或额外的预训练(因为它只使用基因文本的embedding);

(iii)它非常易于使用并生成基因和细胞embedding表示。具体而言,GenePT 使用基于 LLM 的嵌入表示作为与基于表达的数据和表示的正交信息源

2.基因文本获取以及转成embedding

作者提到,基因的文本可以是:

  • 1.symbol的名称,比如CD24;
  • 2.NCBI基因数据库提供的基因summary,如:This gene encodes a sialoglycoprotein that is expressed on mature granulocytes and B cells and modulates growth and differentiation signals to these cells…
  • 3.ChatGPT生成对基因的描述

2.1 词库:

作者统一了 Geneformer 和 scGPT 中提供的基因名称列表。在 Geneformer 的例子中,基因以 Ensembl ID 而非基因symbol名称表示,使用 mygene 包进行转换。

作者提供了词库文件,在github的input_data/gene_info_table.csv中。

2.2 NCBI基因数据库基因summary文本


https://www.ncbi.nlm.nih.gov/gene

作者提供了从NCBI基因数据库获取基因summary的代码(实际上是BeautifulSoup爬取检索到的网页源代码),代码的位置在request_ncbi_text_for_genes.ipynb,很像ChatGPT写的代码哈哈

import requests
from bs4 import BeautifulSoup
import html2text
import mygene
import json
import pickle
mg = mygene.MyGeneInfo()

parts_to_remove = [
    "##  Summary\n",
    "NEW",
    'Try the newGene table',
    'Try the newTranscript table',
    '**',
    "\nGo to the top of the page Help\n"
]

def rough_text_from_gene_name(gene_number):
    
    # 获取网页
    url = f"https://www.ncbi.nlm.nih.gov/gene/{gene_number}"
    summary_text = ''
    soup = None
    try:
        response = requests.get(url, timeout=30)
    except requests.exceptions.Timeout:
        print('time out')
        return((summary_text,soup))
    # 确保检索网页正确
    if response.status_code == 200:
        soup = BeautifulSoup(response.content, 'html.parser')

        # 查找"summary" tab 目录
        summary_tab = soup.find('div', {'class': 'rprt-section gene-summary'})
        if summary_tab:
            html_to_text = html2text.HTML2Text()
            html_to_text.ignore_links = True  # Ignore hyperlinks

            # 提取对应部分的tab
            summary_text = html_to_text.handle(str(summary_tab))
            # 去除不需要的文本部分
            for part in parts_to_remove:
                summary_text = summary_text.replace(part, ' ')
                # 去掉多余的换行符号
            summary_text = summary_text.replace('\n', ' ')

            # 去掉多个空格
            summary_text = ' '.join(summary_text.split())
        else:
            print("Summary tab not found on the page.")
    else:
        print(f"Failed to retrieve the webpage. Status code: {response.status_code}")
    return((summary_text,soup))

用CD24基因作为一个例子:

cd_24_name = mg.querymany('CD24', scopes='symbol', species='human')

gene_name_to_tax_id = {}
for result in cd_24_name:
    if "_id" in result and "query" in result:
        gene_name_to_tax_id[result['symbol']] = result['_id']

# 可以看到是本地运行的
with open('/Users/yiquntchen/Downloads/human/vocab.json', 'rb') as handle:
    vocab_gene = json.load(handle)
vocab_gene_list = list(vocab_gene.keys())

gene_name_to_summary_page = {}

for gene_name, page_id in sorted(gene_name_to_tax_id.items()):
    if gene_name not in gene_name_to_summary_page:
        print('gene_name',gene_name)
        parsed_text, unparsed_html = rough_text_from_gene_name(page_id)
        gene_name_to_summary_page[gene_name] = parsed_text

需要的部分就是红框中的:

以下代码就是批量获取基因symbol的summary了,是依照词库来获取的:

# 导入scGPT的词库
with open(f"{data_dir}/vocab.json', 'rb') as handle:
    vocab_gene = json.load(handle)
vocab_gene_list = list(vocab_gene.keys())

# 导入Geneformer的词库
with open(f"{data_dir}/token_dictionary.pkl", 'rb') as handle:
    token_dictionary = pickle.load(handle)

vocab_gene_list_results = mg.querymany(sorted(vocab_gene_list), scopes='symbol', species='human')
token_dictionary_results = mg.querymany(sorted(token_dictionary.keys()), fields="symbol")

2.3 用ChatGPT来获取基因的summary文本

该代码作者提供了,在gene_embeddings_examples.ipynb代码中:

gene_name_to_GPT_response = {}

gene_completion_test = ['ALPP']
for gene in sorted(gene_completion_test):
    if gene not in gene_name_to_GPT_response:
        print('gene name',gene)
        completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", 
                    messages=[{"role": "user", "content": f"Tell me about gene {gene}"}])
        gene_name_to_GPT_response[gene] = completion.choices[0].message.content
        
gene_name_to_GPT_response['ALPP']

2.4 用ChatGPT3.5模型生成文本embedding

该代码在gene_embeddings_examples.ipynb中,模型使用text-embedding-ada-002,文本的embedding维度是1536(作者自定义),需要自备Open AI的API:

import json
import numpy as np
import openai
# remember to set your OpenAI api key
openai.api_key = ''

def get_gpt_embedding(text, model="text-embedding-ada-002"):
    text = text.replace("\n", " ")
    return np.array(openai.Embedding.create(input = [text], model=model)['data'][0]['embedding'])

with open(f"{data_dir}NCBI_cleaned_summary_of_genes.json", 'r') as file:
    NCBI_cleaned_summary_of_genes = json.load(file)

gpt_gene_name_to_embedding_clean_text = {}

GPT_DIM = 1536 # 自定义embedding的维度1536
for key, text in sorted(NCBI_cleaned_summary_of_genes.items()):
    if key not in gpt_gene_name_to_embedding_clean_text:
        print('key',key)
        if NCBI_cleaned_summary_of_genes[key] == '': 
            # if the dictionary does not have information about a gene
            NCBI_cleaned_summary_of_genes[key] = np.zeros(GPT_DIM) # it's hard coded to be 0
        else:
            NCBI_cleaned_summary_of_genes[key] = get_gpt_embedding(text) 

with open(f"{data_dir}GPT_3_5_gene_embeddings.pickle", "wb") as fp:
    pickle.dump(gpt_gene_name_to_embedding_clean_text, fp)

其实后续这里可以用其它免费的API,比如DeepSeek V3、千问、Hugging face上也有很多免费开源的把文本转换成embedding的模型。

3.模型架构

GenePT有2种模型,分别为GenePT-w和GenePT-s

3.1 GenePT-w

这个模型是需要对每个基因的文本embedding进行加权运算。

  • 1.首先对原本的基因表达矩阵做个预处理(scanpy标准流程)

  • 2.然后对 GenePT 基因文本embedding进行加权平均,权重由每个基因的归一化表达量决定(就是上面的scanpy处理之后的结果)

  • 3.对于一个特定的细胞,获取该细胞中所有基因的归一化表达量;将每个基因的嵌入向量乘以其对应的归一化表达量;将所有加权后的基因嵌入向量相加;对得到的向量进行归一化,使其具有单位ℓ2范数。这意味着将向量的每个分量除以向量的长度(欧几里得范数),使得向量的长度变为1。

举个例子,假设一个细胞有3个基因A、B、C,它们归一化之后的表达量是0.5、0.3、0.2,对应的基因文本embedding向量是 E a E_a Ea E b E_b Eb E c E_c Ec

  • 1.加权:基因A(0.5* E a E_a Ea),基因B(0.3* E b E_b Eb),基因C(0.2* E c E_c Ec)
  • 2.求和:ALL = 0.5* E a E_a Ea+0.3* E b E_b Eb+0.2* E c E_c Ec
  • 3.归一化:(0.5* E a E_a Ea)/ALL,(0.3* E b E_b Eb)/ALL,(0.2* E c E_c Ec)/ALL

这样就得到了GenePT-w的embedding,所以该模型只关注有表达的那部分基因。

最后用已有的聚类方法做细胞聚类,用逻辑回归和随即森林做细胞注释和疾病状态分类等下游任务。

3.2 GenePT-S

不用计算权重,在对scanpy预处理之后的基因表达矩阵的每个细胞中,基因表达值从大到小排序,然后每个细胞提取前N个基因(自定义),这样就组成了cell句子。

然后用基因名称,或NCBI基因summary,或ChatGPT生成的基因描述,替换每个细胞中对应基因的位置,送入到GPT的文本生成embedding模型中获取GenePT-s embedding,本质上就是cell embedding。

最后也是用已有的聚类方法做细胞聚类,用逻辑回归和随即森林做细胞注释和疾病状态分类等下游任务。

4.研究结果

GenePT embedding在结果上效果很好,有时甚至超越 Geneformer,尽管Geneformer是从大型预训练数据集和更复杂的分类头。而且,仅包含基因名称的 GPT-3.5 embedding在某些任务中也表现出很高的准确率。

在细胞类型、癌症类型和捐赠者年龄分类任务中的AMI 和 ARI 指标方面, GenePT-w 和 scGPT 的潜在表示远远优于 GenePT-s 和 Geneformer 的embedding:

在疾病表型预测的分类任务中,GenePT有更好的表现:

细胞聚类和细胞注释效果展示:

批次效应去除的效果展示:

总的来说,文章的思路很巧妙,借助已有大语言模型的方法,能在本地上进行模型的训练,很酷很机智,因为自己从头训练获得cell embedding是很需要算力支撑的,大部分实验室/课题组无法满足。而这篇文章仅2个人就完成了。赞!

在这里插入图片描述


http://www.kler.cn/a/469464.html

相关文章:

  • Docker图形化界面工具Portainer最佳实践
  • 《AI 造梦:解锁虚拟场景与角色逼真丰富密码》
  • unity学习8:unity的基础操作 和对应shortcut
  • Ant Design Pro写项目的总结经验(react)
  • ECCV`24 | 首次解决文本到3D NeRFs分解问题!港中文等提出DreamDissector
  • 鸿蒙应用开发搬砖经验之—使用DevTools工具调试前端页面
  • 【设计模式-1】软件设计模式概述
  • k8s修改存储目录-介绍
  • Docker 安装Elasticsearch搜索引擎 搜索优化 词库挂载 拼音分词 插件安装
  • Linux 防火墙:守护系统安全的坚固防线
  • 今日头条ip属地根据什么显示?不准确怎么办
  • 渗透测试--Web基础漏洞利用技巧
  • 浅谈棋牌游戏开发流程七:反外挂与安全体系——守护游戏公平与玩家体验
  • C# 设计模式(行为型模式):解释器模式
  • ✅binlog、redolog和undolog区别?
  • 深入理解 WebSocket:实时通信的基础
  • 鸿蒙应用开发(2)
  • Java字符编码与正则表达式深度解析
  • Web Services 简介
  • Colyseus 与 HTTP API 的集成
  • 我的Java-Web进阶--SpringMVC
  • Unity加载CSV配置表
  • 前端同步发送HTTP请求 Ajax、Fetch和Axios实现HTTP同步请求
  • YOLOv5部署到web端(flask+js简单易懂)
  • 第27天:Web开发-PHP应用原生语法全局变量数据接受身份验证变量覆盖任意上传
  • 谷歌浏览器的安全检测工具使用指南