LLM 大模型入门笔记-Tokenizer

news2024/11/24 6:49:35

下图展示了完整的 tokenization 流程,接下来会对每个步骤做进一步的介绍。

tokenizer_pipeline

tokenizer_pipeline

1. Normalization

normalize 其实就是根据不同的需要对文本数据做一下清洗工作,以英文文本为例可以包括删除不必要的空白、小写和/或删除重音符号。

代码语言:javascript

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))

>>> 'hello how are u?'

2. Pre-tokenization

数据清洗好后,我们需要将文本作划分。对于英语而言,最简单的划分逻辑就是以单词为单位进行划分。不过即使是这么简单的规则也可以细分出很多不同的划分方式,下面展示了 3 种划分方式,它们适用于不同的模型训练,返回的是一个 list,每个元素是一个tuple。tuple 内第一个元素是划分后的sub-word,第二个元素是其初始和结尾的索引。

  • bert 的最简单,真的就是最符合直觉的 huafenfangshi
  • gpt2划分的不同点是单词前如果有空格的话,空格会转换成一个特殊字符,即 Ġ
  • t5 类似 gpt2 也考虑了空格,不过空格被替换成了 _

3. BPE Tokenization

上面Pre-tokenization展示的是比较简单的划分方式,但是他们的缺点是会导致词表非常大。而且,我们知道英文单词是有词根的,并且一个动词会有不同的时态,简单的以单词为单位划分,不太便于表示单词之间的相似性。所以一种可行的办法是我们寻找单词间的公约数,即把单词拆分成若干个 sub-word。为方便理解,我们可以以 like, liked, liking 为例,这三者的公约数是 lik, 所以分别可以拆分成如下(实际上的拆分并不一定就是下面的结果,这里只是为了方便解释说明):

  • [“lik”, “e”]
  • [“lik”, “ed”]
  • [“lik”, “ing”]

模型在计算这三个单词的相似性的时候,因为他们具有相同的"lik",所以肯定会认为有很高的相似性。类似的,当模型计算两个都带有"ed"的单词的时候,也会知道这两个单词也会有相似性,因为都表示过去式。

那么如何寻找公约数呢?大佬们提出了不同的算法,常见的三个算法总结在下表里了:

3.1 BPE 原理解释

这一小节我们着重介绍一下最常见的算法之一:BPE (Byte-pair Encoding)。huggingface官方tutorial 给出了非常详细的解释,这里做一个简单的介绍。

BPE 其实是一个统计算法,不同意深度神经网络,只要给定一个数据集或者一篇文章,BPE 不管运行多少次都会得出同样的结果。下面我们看看 BPE 到底是在做什么。

为了方便理解,我们假设我们的语料库中只有下面 5 个单词,数字表示出现的频率:

语料库:[("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)]

BPE 根据上述单词表首先初始化生成基础词汇表(base vocabulary),即

词汇表:["b", "g", "h", "n", "p", "s", "u"]

我们可以将每个单词看成是一个由多个基础 token 组成的 list,即

[("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)]

接下来,正如 BPE 的名字 byte-pair 所表示的,它会对每个单词内部相邻的 token 逐一进行两两匹配,然后找出出现频率最高的那一对,例如,("h" "u" "g", 10) 匹配结果会到的 ("h", "u", 10)("u", "g", 10),其他单词同理。

通过遍历所有单词我们可以发现出现频率最高的 ("u", "g"),它在 “hug”、“pug” 和 “hugs” 中出现,总共出现了 20 次,所以 BPE 会将它们进行合并(merge),即 ("u", "g") -> "ug"。这样基础词汇表就可以新增一个 token 了,更新后的词汇表和语料库如下:

代码语言:javascript

词汇表:["b", "g", "h", "n", "p", "s", "u", "ug"]
语料库:("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)

我们继续重复上面的 遍历和合并 操作,每次词汇表都会新增一个 token。当词汇表内 token 数量达到预设值的时候就会停止 BPE 算法了,并返回最终的词汇表和语料库。

3.2 BPE 代码实战

3.2.1. 初始化一个简单的文本数据集,如下

代码语言:javascript

corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
3.2.2. pre-tokenization (初始化语料库和词汇表)
  • 语料库

normalize 步骤就省略了。我们直接先构建一下语料库,以单词为单位对原始文本序列进行划分,并统计每个单词的频率。

代码语言:javascript

from transformers import AutoTokenizer
from collections import defaultdict

tokenizer = AutoTokenizer.from_pretrained("gpt2")
word_freqs = defaultdict(int)

for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

print(word_freqs)

>>> defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
    'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
    'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
    'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
  • 词汇表

代码语言:javascript

alphabet = []

for word in word_freqs.keys():
    for letter in word:
        if letter not in alphabet:
            alphabet.append(letter)
alphabet.sort()
vocab = ["<|endoftext|>"] + alphabet.copy()

print(vocab)

>>> ['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
  't', 'u', 'v', 'w', 'y', 'z', 'Ġ']

根据词汇表将语料库进行进一步的划分,即把每一个单词表示成由多个 token(或 sub-word)组成的 list:

代码语言:javascript

splits = {word: [c for c in word] for word in word_freqs.keys()}
3.2.3 BPE 合并字典和词汇表

遍历搜索,找到出现频率最高的 byte-pair

代码语言:javascript

def compute_pair_freqs(splits):
    pair_freqs = defaultdict(int)
    for word, freq in word_freqs.items():
        split = splits[word]
        if len(split) == 1:
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            pair_freqs[pair] += freq
    return pair_freqs

pair_freqs = compute_pair_freqs(splits)
best_pair = ""
max_freq = None

for pair, freq in pair_freqs.items():
    if max_freq is None or max_freq < freq:
        best_pair = pair
        max_freq = freq

print(best_pair, max_freq)

>>> ('Ġ', 't') 7

更新词汇表和初始化合并字典,该字典记录了整个合并的过程;

代码语言:javascript

vocab.append("Ġt")
merges = {("Ġ", "t"): "Ġt"}

根据新增合并规则更新语料库

代码语言:javascript

def merge_pair(a, b, splits):
    for word in word_freqs:
        split = splits[word]
        if len(split) == 1:
            continue

        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                split = split[:i] + [a + b] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits

splits = merge_pair("Ġ", "t", splits)
print(splits["Ġtrained"])

>>> ['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']

总结一下上述步骤,我们找到了出现频率最高的一组 byte-pair,由此更新了词汇表和语料库。接下来,我们重复上述过程,不断增加词汇表的大小,直到词汇表包含 50 个 token 为止:

代码语言:javascript

vocab_size = 50

while len(vocab) < vocab_size:
    pair_freqs = compute_pair_freqs(splits)
    best_pair = ""
    max_freq = None
    for pair, freq in pair_freqs.items():
        if max_freq is None or max_freq < freq:
            best_pair = pair
            max_freq = freq
    splits = merge_pair(*best_pair, splits)
    merges[best_pair] = best_pair[0] + best_pair[1]
    vocab.append(best_pair[0] + best_pair[1])


print(vocab)
>>> ['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']

print(merges)
>>> {('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
 ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
 ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
 ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
3.2.4 tokenize 文本数据

至此,我们完成了对给定文本数据的 BPE 算法,得到了长度为 50 的词汇表和语料库。那么该如何利用生成的词汇表和语料库对新的文本数据做 tokenization 呢?代码如下:

代码语言:javascript

def tokenize(text):
    pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in pre_tokenize_result]
    splits = [[l for l in word] for word in pre_tokenized_text]
    for pair, merge in merges.items():
        for idx, split in enumerate(splits):
            i = 0
            while i < len(split) - 1:
                if split[i] == pair[0] and split[i + 1] == pair[1]:
                    split = split[:i] + [merge] + split[i + 2 :]
                else:
                    i += 1
            splits[idx] = split

    return sum(splits, [])

tokenize("This is not a token.")
>>> ['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.']
3.2.5. tokenize 的逆(decode)过程

借助前面生成的 merge 字典,我们可以实现 tokenize的逆过程,这通常是在处理模型预测结果的时候需要用到,代码如下:

代码语言:javascript

def detokenize(tokens, merges):
    reconstructed_text = ''.join(tokens)
    for pair, merge in merges.items():
        reconstructed_text = reconstructed_text.replace(merge, pair[0] + pair[1])
    return reconstructed_text.replace('Ġ', ' ')

# 假设 merges 是你之前代码中使用的 merges 字典
merges = {('u', 'g'): 'ug', ('u', 'n'): 'un', ('h', 'ug'): 'hug'}  # 举例的 merges 字典

tokens = tokenize("This is not a token.")  # 假设 tokens 是之前 tokenize 函数的输出结果
original_text = detokenize(tokens, merges)
print(original_text)
>>> This is not a token.

如何系统的去学习大模型LLM ?

作为一名热心肠的互联网老兵,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。

但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的 AI大模型资料 包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来

所有资料 ⚡️ ,朋友们如果有需要全套 《LLM大模型入门+进阶学习资源包》,扫码获取~

👉CSDN大礼包🎁:全网最全《LLM大模型入门+进阶学习资源包》免费分享(安全链接,放心点击)👈

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

在这里插入图片描述

四、AI大模型商业化落地方案

img

阶段1:AI大模型时代的基础理解

  • 目标:了解AI大模型的基本概念、发展历程和核心原理。
  • 内容
    • L1.1 人工智能简述与大模型起源
    • L1.2 大模型与通用人工智能
    • L1.3 GPT模型的发展历程
    • L1.4 模型工程
      - L1.4.1 知识大模型
      - L1.4.2 生产大模型
      - L1.4.3 模型工程方法论
      - L1.4.4 模型工程实践
    • L1.5 GPT应用案例

阶段2:AI大模型API应用开发工程

  • 目标:掌握AI大模型API的使用和开发,以及相关的编程技能。
  • 内容
    • L2.1 API接口
      - L2.1.1 OpenAI API接口
      - L2.1.2 Python接口接入
      - L2.1.3 BOT工具类框架
      - L2.1.4 代码示例
    • L2.2 Prompt框架
      - L2.2.1 什么是Prompt
      - L2.2.2 Prompt框架应用现状
      - L2.2.3 基于GPTAS的Prompt框架
      - L2.2.4 Prompt框架与Thought
      - L2.2.5 Prompt框架与提示词
    • L2.3 流水线工程
      - L2.3.1 流水线工程的概念
      - L2.3.2 流水线工程的优点
      - L2.3.3 流水线工程的应用
    • L2.4 总结与展望

阶段3:AI大模型应用架构实践

  • 目标:深入理解AI大模型的应用架构,并能够进行私有化部署。
  • 内容
    • L3.1 Agent模型框架
      - L3.1.1 Agent模型框架的设计理念
      - L3.1.2 Agent模型框架的核心组件
      - L3.1.3 Agent模型框架的实现细节
    • L3.2 MetaGPT
      - L3.2.1 MetaGPT的基本概念
      - L3.2.2 MetaGPT的工作原理
      - L3.2.3 MetaGPT的应用场景
    • L3.3 ChatGLM
      - L3.3.1 ChatGLM的特点
      - L3.3.2 ChatGLM的开发环境
      - L3.3.3 ChatGLM的使用示例
    • L3.4 LLAMA
      - L3.4.1 LLAMA的特点
      - L3.4.2 LLAMA的开发环境
      - L3.4.3 LLAMA的使用示例
    • L3.5 其他大模型介绍

阶段4:AI大模型私有化部署

  • 目标:掌握多种AI大模型的私有化部署,包括多模态和特定领域模型。
  • 内容
    • L4.1 模型私有化部署概述
    • L4.2 模型私有化部署的关键技术
    • L4.3 模型私有化部署的实施步骤
    • L4.4 模型私有化部署的应用场景

学习计划:

  • 阶段1:1-2个月,建立AI大模型的基础知识体系。
  • 阶段2:2-3个月,专注于API应用开发能力的提升。
  • 阶段3:3-4个月,深入实践AI大模型的应用架构和私有化部署。
  • 阶段4:4-5个月,专注于高级模型的应用和部署。
这份完整版的所有 ⚡️ 大模型 LLM 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

全套 《LLM大模型入门+进阶学习资源包↓↓↓ 获取~

👉CSDN大礼包🎁:全网最全《LLM大模型入门+进阶学习资源包》免费分享(安全链接,放心点击)👈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1879754.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《概率论与数理统计》期末复习笔记_下

目录 第4章 随机变量的数字特征 4.1 数学期望 4.2 方差 4.3 常见分布的期望与方差 4.4 协方差与相关系教 第5章 大数定律和中心极限定理 5.1 大数定律 5.2 中心极限定理 第6章 样本与抽样分布 6.1 数理统汁的基本概念 6.2 抽样分布 6.2.1 卡方分布 6.2.2 t分布 6.…

Winform使用HttpClient调用WebApi的基本用法

Winform程序调用WebApi的方式有很多&#xff0c;本文学习并记录采用HttpClient调用基于GET、POST请求的WebApi的基本方式。WebApi使用之前编写的检索环境检测数据的接口&#xff0c;如下图所示。 调用基于GET请求的无参数WebApi 创建HttpClient实例后调用GetStringAsync函数获…

数学之美:SQL语句的编译与关系代数

引言 当年读书的时候&#xff0c;真正学到数据库的操作之前&#xff0c;先学的内容是关系代数运算&#xff0c;以及相关的关系代数的定律。然后知道了当前比较主流的数据库都是关系型数据库&#xff0c;其底层依赖的是关系代数。 但是&#xff0c;当年考试的时候&#xff0c;…

【C语言】C语言-体育彩票的模拟生成和兑奖(源码+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

研导智能科技——AI辅助科研产品开发

人工智能&#xff08;AI&#xff09;技术的飞速发展为科研领域带来了革命性的变化。本公司致力于开发基于人工智能的科研辅助产品&#xff0c;旨在通过智能化手段提高科研人员的工作效率和研究质量。目前&#xff0c;我们成功开发了研导学术平台&#xff08;www.zhiyanxueshu.c…

Clickhouse启动失败定位

Clickhouse启动失败定位 1. 定位问题 查看状态 systemctl status clickhouse-server2. 查看日志 在这里插入代码片3. 发现是磁盘不够&#xff0c;进一步查看磁盘信息 df -h 目录4. 查看目录存储信息 du -h --max-depth1 /data/clickhouse5. 进行磁盘清理

VMware每次打开网络设置都出现需要运行NetworkManager问题

每次打开都出现这个情况&#xff0c;是因为之前把NetworkManager服务服务关闭&#xff0c;重新输入命令&#xff1a; sudo systemctl start NetworkManager.service或者 sudo service network-manager restart 即可解决&#xff0c;但是每次开机重启都要打开就很麻烦&#xf…

Webpack: 构建 NPM Library

概述 虽然 Webpack 多数情况下被用于构建 Web 应用&#xff0c;但与 Rollup、Snowpack 等工具类似&#xff0c;Webpack 同样具有完备的构建 NPM 库的能力。与一般场景相比&#xff0c;构建 NPM 库时需要注意&#xff1a; 正确导出模块内容&#xff1b;不要将第三方包打包进产…

面了英伟达算法岗,被疯狂拷打。。。

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、算法项目落地经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接如…

Web Based Quiz System v1.0 SQL 注入漏洞(CVE-2022-32991)

前言 CVE-2022-32991 是一个影响 Web Based Quiz System v1.0 的 SQL 注入漏洞。这个漏洞存在于 welcome.php 文件中的 eid 参数处。攻击者可以通过此漏洞在数据库中执行任意 SQL 语句&#xff0c;从而获取、修改或删除数据库中的数据。 具体细节如下&#xff1a; 攻击向量&…

Websocket解析及用法(封装一个通用订阅发布主题的webSocket类)

1、什么是WebSocket? websocket的目标是通过一个长连接实现与服务器全双工&#xff0c;双向的通信。是一种在单个TCP连接上进行全双工通信的协议&#xff0c;使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在 js中创建websocket…

改机软件有哪些?实现一键新机、改串号、改IMEI和手机参数的需求 硬改手机软件,新机环境模拟 设备伪装,一键改机,一键复原

这次针对可以直接开端口修改参数的机型做一些工具解析 前面接触合作过很多工作室。其中很多工作室对于各自软件的跳验证有各自的需求。 一个机型各项参数一般有IMEI WiFi 蓝牙 sn psb ESN等等。 针对这些参数的修改首先要明白各自软件检测的具体是哪些参数来验证。 对于常用…

解决IDEA的Web项目右键无法创建Servlet问题

右键新建没有servlet? 在pom.xml文件中需要导入servlet依赖&#xff0c;很简单的&#xff0c;别担心&#xff0c;就20秒解决 看我操作&#xff01;&#xff01;&#xff01; 1. 找到自动生成的pom.xml文件 只要你创建了maven项目&#xff0c;就会自动生成pom.xml文件&#xf…

基于Java废物回收机构管理系统详细设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

python多继承的3C算法

python多继承的3C算法 有很多地方都说python多继承的继承顺序&#xff0c;是按照深度遍历的方式&#xff0c;其实python多继承顺序的算法&#xff0c;不是严格意义上的深度遍历&#xff0c;而是基于深度遍历基础上优化出一种叫3C算法 python多继承的深度遍历 class C:def ru…

Solidworke学习(齿轮绘画)

目录 一、齿轮的基础理论 二、齿轮啮合传动的条件 三、传送比 四、绘画齿轮的步骤 五、绘画齿数小的齿轮 学习链接&#xff1a;徒手画齿轮配合&#xff0c;solidworks超简单3D打印齿轮画法_哔哩哔哩_bilibili 一、齿轮的基础理论 齿轮的啮合通过渐开线之间的互相挤压实现…

【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

VRay是什么?有什么特点?渲染100邀请码1a12

Vray是由Chaos Group开发的高性能渲染引擎&#xff0c;能为不同的三维建模软件提供图像和动画渲染服务&#xff0c;它有以下几个特点。 1、Vray采用了先进的光线追踪技术&#xff0c;能够模拟真实世界中光线的传播和反射&#xff0c;生成的图像和动画十分逼真。 2、Vray提供了…

【C++】 ——【模板初阶】——基础详解

目录 1. 泛型编程 1.1 泛型编程的概念 1.2 泛型编程的历史与发展 1.3 泛型编程的优势 1.4 泛型编程的挑战 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 2.6 函数模板的特化 2.7 函数模板的使…

目前常见的几款企业级im即时通讯软件有哪些?

在目前的市场上&#xff0c;有几款常见的企业级即时通讯软件广受企业青睐。以下是其中几款主流企业级即时通讯软件的介绍和特点。 1. 微软Teams 微软Teams是一款集即时通讯、协作和视频会议功能于一体的企业级通讯软件。它提供了实时聊天、语音通话、视频会议、文件共享和团队…