|
|
--- |
|
|
license: mit |
|
|
language: |
|
|
- en |
|
|
- zh |
|
|
pipeline_tag: token-classification |
|
|
--- |
|
|
# bert-chunker-Chinese-2 |
|
|
|
|
|
[GitHub](https://github.com/jackfsuia/bert-chunker/tree/main/bcc2) |
|
|
|
|
|
|
|
|
bert-chunker-Chinese-2 (中文分段器) is a text chunker based on BertForTokenClassification to predict the start token of chunks (for use in RAG, etc), and using a sliding window it cuts documents of any size into chunks. We see it as an alternative of [semantic chunker](https://github.com/FullStackRetrieval-com/RetrievalTutorials/blob/main/tutorials/LevelsOfTextSplitting/5_Levels_Of_Text_Splitting.ipynb), but specially, it not only works for the structured texts, but also the **unstructured and messy texts**. It is a new version of [bc-chinese](https://huggingface.co/tim1900/bert-chunker-chinese), for which we change our data labeling and train pipeline to make it more stable and useful. |
|
|
|
|
|
Updates : |
|
|
- 2025.5.12: an experimental script that **supports specifying the maximum tokens per chunk** is available now [below](#experimental). |
|
|
## Usage |
|
|
Run the following: |
|
|
|
|
|
```python |
|
|
# -*- coding: utf-8 -*- |
|
|
import torch |
|
|
from transformers import AutoTokenizer, BertForTokenClassification |
|
|
import math |
|
|
|
|
|
model_path = "tim1900/bert-chunker-Chinese-2" |
|
|
|
|
|
tokenizer = AutoTokenizer.from_pretrained( |
|
|
model_path, |
|
|
padding_side="right", |
|
|
model_max_length=512, |
|
|
trust_remote_code=True, |
|
|
) |
|
|
|
|
|
device = "cpu" # or 'cuda' |
|
|
|
|
|
model = BertForTokenClassification.from_pretrained( |
|
|
model_path, |
|
|
).to(device) |
|
|
|
|
|
def chunk_text(model, text, tokenizer, prob_threshold=0.5): |
|
|
# slide context window chunking |
|
|
MAX_TOKENS = 512 |
|
|
tokens = tokenizer(text, return_tensors="pt", truncation=False) |
|
|
input_ids = tokens["input_ids"] |
|
|
attention_mask = tokens["attention_mask"][:, 0:MAX_TOKENS] |
|
|
attention_mask = attention_mask.to(model.device) |
|
|
CLS = input_ids[:, 0].unsqueeze(0) |
|
|
SEP = input_ids[:, -1].unsqueeze(0) |
|
|
input_ids = input_ids[:, 1:-1] |
|
|
model.eval() |
|
|
split_str_poses = [] |
|
|
token_pos = [] |
|
|
windows_start = 0 |
|
|
windows_end = 0 |
|
|
logits_threshold = math.log(1 / prob_threshold - 1) |
|
|
print(f"Processing {input_ids.shape[1]} tokens...") |
|
|
while windows_end <= input_ids.shape[1]: |
|
|
windows_end = windows_start + MAX_TOKENS - 2 |
|
|
|
|
|
ids = torch.cat((CLS, input_ids[:, windows_start:windows_end], SEP), 1) |
|
|
|
|
|
ids = ids.to(model.device) |
|
|
|
|
|
output = model( |
|
|
input_ids=ids, |
|
|
attention_mask=torch.ones(1, ids.shape[1], device=model.device), |
|
|
) |
|
|
logits = output["logits"][:, 1:-1, :] |
|
|
chunk_decision = logits[:, :, 1] > (logits[:, :, 0] - logits_threshold) |
|
|
greater_rows_indices = torch.where(chunk_decision)[1].tolist() |
|
|
|
|
|
# null or not |
|
|
if len(greater_rows_indices) > 0 and ( |
|
|
not (greater_rows_indices[0] == 0 and len(greater_rows_indices) == 1) |
|
|
): |
|
|
|
|
|
split_str_pos = [ |
|
|
tokens.token_to_chars(sp + windows_start + 1).start |
|
|
for sp in greater_rows_indices |
|
|
if sp > 0 |
|
|
] |
|
|
token_pos += [ |
|
|
sp + windows_start + 1 for sp in greater_rows_indices if sp > 0 |
|
|
] |
|
|
split_str_poses += split_str_pos |
|
|
|
|
|
windows_start = greater_rows_indices[-1] + windows_start |
|
|
|
|
|
else: |
|
|
|
|
|
windows_start = windows_end |
|
|
|
|
|
substrings = [ |
|
|
text[i:j] for i, j in zip([0] + split_str_poses, split_str_poses + [len(text)]) |
|
|
] |
|
|
token_pos = [0] + token_pos |
|
|
return substrings, token_pos |
|
|
|
|
|
|
|
|
# chunking |
|
|
print("\n>>>>>>>>> Chunking...") |
|
|
doc = r'''经典中式家常菜:红烧肉 详尽版食谱 |
|
|
|
|
|
红烧肉,作为中华美食殿堂中一颗璀璨的明珠,以其色泽红亮、肥而不腻、入口即化的独特风味,征服了无数食客的味蕾。它不仅仅是一道菜,更是一种情怀,是记忆中妈妈厨房里飘出的诱人香气,是团圆饭桌上最温暖的慰藉。要烹制出一盘完美的红烧肉,需要耐心、技巧以及对细节的专注。以下将为您详尽分解从选材到成品的每一个步骤,力求让您在家也能复刻出餐厅级别的美味。本食谱旨在深入剖析,故篇幅较长,以确保每个环节都清晰透彻。 |
|
|
|
|
|
一、 精选主料:奠定美味的基石 |
|
|
|
|
|
• 猪肉选择。 |
|
|
|
|
|
◦ 首选带皮的五花肉。 |
|
|
|
|
|
◦ 层次分明为佳。 |
|
|
|
|
|
◦ 肥瘦相间是关键。 |
|
|
|
|
|
◦ 厚度约三指左右。 |
|
|
|
|
|
◦ 重量约一斤半为宜。 |
|
|
|
|
|
◦ 确保猪皮完整无缺。 |
|
|
|
|
|
◦ 新鲜肉品呈鲜红色。 |
|
|
|
|
|
◦ 脂肪部分应洁白细腻。 |
|
|
|
|
|
• 预处理工作。 |
|
|
|
|
|
◦ 用镊子拔除残留猪毛。 |
|
|
|
|
|
◦ 将肉块置于冷水中浸泡。 |
|
|
|
|
|
◦ 浸泡时间约三十分钟。 |
|
|
|
|
|
◦ 中途可换水一至两次。 |
|
|
|
|
|
◦ 目的是泡出部分血水。 |
|
|
|
|
|
◦ 然后用刀刮洗猪皮表面。 |
|
|
|
|
|
◦ 彻底清除污物和杂质。 |
|
|
|
|
|
◦ 最后用流动水冲洗干净。 |
|
|
|
|
|
二、 准备辅料:调配灵魂之味 |
|
|
|
|
|
• 主要调味品。 |
|
|
|
|
|
◦ 老抽:负责上色。 |
|
|
|
|
|
◦ 用量需谨慎控制。 |
|
|
|
|
|
◦ 过多会导致发黑发苦。 |
|
|
|
|
|
◦ 生抽:提供咸鲜底味。 |
|
|
|
|
|
◦ 与老抽比例约二比一。 |
|
|
|
|
|
◦ 优质花雕酒是精髓。 |
|
|
|
|
|
◦ 去腥增香效果显著。 |
|
|
|
|
|
◦ 冰糖:首选黄冰糖。 |
|
|
|
|
|
◦ 炒出的糖色更红亮。 |
|
|
|
|
|
◦ 风味也比白糖醇和。 |
|
|
|
|
|
• 香辛料组合。 |
|
|
|
|
|
◦ 生姜一大块,切片。 |
|
|
|
|
|
◦ 大葱一根,切成长段。 |
|
|
|
|
|
◦ 蒜头数瓣,轻轻拍松。 |
|
|
|
|
|
◦ 八角两至三颗,增香。 |
|
|
|
|
|
◦ 桂皮一小段,勿过多。 |
|
|
|
|
|
◦ 香叶两片,增添风味。 |
|
|
|
|
|
◦ 草果一颗,可拍裂开。 |
|
|
|
|
|
◦ 干辣椒依个人口味添加。 |
|
|
|
|
|
• 其他基础材料。 |
|
|
|
|
|
◦ 食用油适量,需耐高温。 |
|
|
|
|
|
◦ 最好使用菜籽油或花生油。 |
|
|
|
|
|
◦ 食盐少许,用于最后调味。 |
|
|
|
|
|
◦ 因为酱油已有咸度。 |
|
|
|
|
|
◦ 准备足量的开水备用。 |
|
|
|
|
|
◦ 切记不可使用冷水。 |
|
|
|
|
|
◦ 冷水会使肉质收缩变柴。 |
|
|
|
|
|
三、 精细加工:关键步骤解析 |
|
|
|
|
|
• 肉块改刀。 |
|
|
|
|
|
◦ 将洗净的五花肉捞出。 |
|
|
|
|
|
◦ 用厨房纸彻底吸干水分。 |
|
|
|
|
|
◦ 这一步非常重要。 |
|
|
|
|
|
◦ 能有效防止后续溅油。 |
|
|
|
|
|
◦ 将肉切成三厘米见方块。 |
|
|
|
|
|
◦ 大小尽量保持均匀一致。 |
|
|
|
|
|
◦ 以确保受热和入味均匀。 |
|
|
|
|
|
◦ 切面应能看到完美层次。 |
|
|
|
|
|
• 焯水去腥。 |
|
|
|
|
|
◦ 冷水下锅,放入切好的肉。 |
|
|
|
|
|
◦ 同时加入几片生姜。 |
|
|
|
|
|
◦ 倒入一汤匙花雕酒。 |
|
|
|
|
|
◦ 开大火煮沸,撇净浮沫。 |
|
|
|
|
|
◦ 浮沫是血水和杂质所致。 |
|
|
|
|
|
◦ 务必撇除干净直至汤清。 |
|
|
|
|
|
◦ 焯水时间约五到八分钟。 |
|
|
|
|
|
◦ 煮至肉块变色定型即可。 |
|
|
|
|
|
• 捞出与冲洗。 |
|
|
|
|
|
◦ 用漏勺将肉块捞出。 |
|
|
|
|
|
◦ 立即放入温水中冲洗。 |
|
|
|
|
|
◦ 洗去表面残留的浮沫。 |
|
|
|
|
|
◦ 注意水温不宜过低。 |
|
|
|
|
|
◦ 再次用厨房纸吸干水分。 |
|
|
|
|
|
◦ 防止入锅时油花四溅。 |
|
|
|
|
|
◦ 此时肉块呈灰白色。 |
|
|
|
|
|
◦ 经过焯水已无肉腥味。 |
|
|
|
|
|
四、 核心工艺:炒糖色与煸炒 |
|
|
|
|
|
• 炒制糖色。 |
|
|
|
|
|
◦ 锅烧热,倒入少量底油。 |
|
|
|
|
|
◦ 放入准备好的冰糖。 |
|
|
|
|
|
◦ 开中小火慢慢搅动。 |
|
|
|
|
|
◦ 观察冰糖融化的过程。 |
|
|
|
|
|
◦ 先从固体变为液态。 |
|
|
|
|
|
◦ 再从小泡转为密集大泡。 |
|
|
|
|
|
◦ 当大泡逐渐回落消失时。 |
|
|
|
|
|
◦ 糖液颜色开始加深。 |
|
|
|
|
|
• 观察颜色变化。 |
|
|
|
|
|
◦ 从浅黄色变为枣红色。 |
|
|
|
|
|
◦ 这个瞬间非常关键。 |
|
|
|
|
|
◦ 枣红色时立即下入肉块。 |
|
|
|
|
|
◦ 过早则甜腻,过晚则发苦。 |
|
|
|
|
|
◦ 动作务必迅速而准确。 |
|
|
|
|
|
◦ 糖色是红亮色泽的来源。 |
|
|
|
|
|
◦ 也是风味层次的基础。 |
|
|
|
|
|
• 煸炒肉块。 |
|
|
|
|
|
◦ 快速颠锅,使每块肉均匀裹上糖色。 |
|
|
|
|
|
◦ 持续翻炒约三到五分钟。 |
|
|
|
|
|
◦ 直到肉块表面微微焦黄。 |
|
|
|
|
|
◦ 部分油脂被煸炒出来。 |
|
|
|
|
|
◦ 这样吃起来肥而不腻。 |
|
|
|
|
|
◦ 同时香味物质充分释放。 |
|
|
|
|
|
◦ 煸出的猪油可倒出部分。 |
|
|
|
|
|
◦ 留作炒青菜风味极佳。 |
|
|
|
|
|
五、 炖煮入味:时间与火候的艺术 |
|
|
|
|
|
• 加入调料。 |
|
|
|
|
|
◦ 沿着锅边烹入花雕酒。 |
|
|
|
|
|
◦ 瞬间激发出浓郁酒香。 |
|
|
|
|
|
◦ 接着倒入适量生抽。 |
|
|
|
|
|
◦ 再加入少许老抽上色。 |
|
|
|
|
|
◦ 放入所有香料:姜、葱、蒜等。 |
|
|
|
|
|
◦ 与肉块一起翻炒均匀。 |
|
|
|
|
|
◦ 让酱香与肉香充分融合。 |
|
|
|
|
|
◦ 翻炒约两分钟至香气扑鼻。 |
|
|
|
|
|
• 注入开水。 |
|
|
|
|
|
◦ 务必一次性加足开水。 |
|
|
|
|
|
◦ 水量要完全没过肉块。 |
|
|
|
|
|
◦ 甚至可以略多一些。 |
|
|
|
|
|
◦ 避免中途再次加水。 |
|
|
|
|
|
◦ 大火烧开后转小火。 |
|
|
|
|
|
◦ 盖上锅盖,慢火焖炖。 |
|
|
|
|
|
◦ 这是“入口即化”的关键。 |
|
|
|
|
|
◦ 时间至少需要一小时。 |
|
|
|
|
|
• 慢炖过程。 |
|
|
|
|
|
◦ 保持汤面微沸即可。 |
|
|
|
|
|
◦ 火候切忌过大过急。 |
|
|
|
|
|
◦ 否则容易烧干且肉不烂。 |
|
|
|
|
|
◦ 期间可偶尔开盖查看。 |
|
|
|
|
|
◦ 用勺子轻轻推动一下。 |
|
|
|
|
|
◦ 防止粘锅底的情况发生。 |
|
|
|
|
|
◦ 但尽量不要频繁翻动。 |
|
|
|
|
|
◦ 以免影响肉块的完整。 |
|
|
|
|
|
六、 收汁与装盘:成就最终美味 |
|
|
|
|
|
• 大火收汁。 |
|
|
|
|
|
◦ 炖煮一小时后。 |
|
|
|
|
|
◦ 用筷子戳一下瘦肉部分。 |
|
|
|
|
|
◦ 若能轻松戳透即表示已软烂。 |
|
|
|
|
|
◦ 此时根据汤汁咸度加盐。 |
|
|
|
|
|
◦ 开大火,将汤汁收浓。 |
|
|
|
|
|
◦ 用锅铲不停搅动。 |
|
|
|
|
|
◦ 防止糊底,并让汤汁变稠。 |
|
|
|
|
|
◦ 均匀包裹在每一块肉上。 |
|
|
|
|
|
• 收汁技巧。 |
|
|
|
|
|
◦ 收到汤汁浓稠如蜜。 |
|
|
|
|
|
◦ 油亮红润的汤汁紧裹肉块。 |
|
|
|
|
|
◦ 锅中泛起密集的大泡。 |
|
|
|
|
|
◦ 即可准备关火出锅。 |
|
|
|
|
|
◦ 收汁程度依个人喜好。 |
|
|
|
|
|
◦ 喜欢拌饭可多留些汤汁。 |
|
|
|
|
|
◦ 整个过程需密切留意。 |
|
|
|
|
|
◦ 最后阶段变化非常迅速。 |
|
|
|
|
|
• 最终成品。 |
|
|
|
|
|
◦ 将红烧肉盛入预热好的盘中。 |
|
|
|
|
|
◦ 可烫几棵小油菜围边。 |
|
|
|
|
|
◦ 既点缀色彩,又解油腻。 |
|
|
|
|
|
◦ 撒上少许葱花或香菜末。 |
|
|
|
|
|
◦ 一道色香味俱全的红烧肉完成。 |
|
|
|
|
|
◦ 肉质软糯,肥而不腻。 |
|
|
|
|
|
◦ 咸中带甜,回味无穷。 |
|
|
|
|
|
◦ 配上一碗白米饭是绝配。 |
|
|
|
|
|
七、 要点总结与升华 |
|
|
|
|
|
• 成功关键。 |
|
|
|
|
|
◦ 选材是基础,务必新鲜。 |
|
|
|
|
|
◦ 焯水步骤不可省略。 |
|
|
|
|
|
◦ 炒糖色是技术核心。 |
|
|
|
|
|
◦ 火候控制是成败关键。 |
|
|
|
|
|
◦ 耐心慢炖是美味保证。 |
|
|
|
|
|
• 变化与创新。 |
|
|
|
|
|
◦ 可加入土豆、鹌鹑蛋同烧。 |
|
|
|
|
|
◦ 吸收肉汤,滋味更丰富。 |
|
|
|
|
|
◦ 也可尝试用啤酒代替水。 |
|
|
|
|
|
◦ 别有一番风味层次。 |
|
|
|
|
|
◦ 但万变不离其宗。 |
|
|
|
|
|
◦ 核心技法仍需掌握。 |
|
|
|
|
|
烹饪是一门需要实践的艺术,红烧肉更是如此。希望这份详尽的食谱能成为您厨房路上的得力助手,愿您能享受从准备到品尝的整个过程,与家人朋友分享这份由时间与匠心凝聚而成的温暖美味。每一次尝试都是一次经验的积累,祝您烹饪愉快,早日成就属于自己的招牌红烧肉! |
|
|
''' |
|
|
# Chunk the text. The prob_threshold should be between (0, 1). The lower it is, the more chunks will be generated. |
|
|
# Therefore adjust it to your need, when prob_threshold is small like 0.000001, each token is one chunk, |
|
|
# when it is set to 1, the whole text is one chunk. |
|
|
chunks, token_pos = chunk_text(model, doc, tokenizer, prob_threshold=0.5) |
|
|
|
|
|
# print chunks |
|
|
for i, (c, t) in enumerate(zip(chunks, token_pos)): |
|
|
print(f"-----chunk: {i}----token_idx: {t}--------") |
|
|
print(c) |
|
|
``` |
|
|
## Experimental |
|
|
The following script supports specifying max tokens per chunk. Chunker will be forced to choose a best possible position from history to chunk when it is about to exceed the max_tokens_per_chunk and no token satisfy the prob_threshold. This script can be seen as a new experimental version of the scripts above. |
|
|
```python |
|
|
# -*- coding: utf-8 -*- |
|
|
import torch |
|
|
from transformers import AutoTokenizer, BertForTokenClassification |
|
|
import math |
|
|
|
|
|
model_path = "tim1900/bert-chunker-Chinese-2" |
|
|
|
|
|
tokenizer = AutoTokenizer.from_pretrained( |
|
|
model_path, |
|
|
padding_side="right", |
|
|
model_max_length=512, |
|
|
trust_remote_code=True, |
|
|
) |
|
|
|
|
|
device = "cpu" # or 'cuda' |
|
|
|
|
|
model = BertForTokenClassification.from_pretrained( |
|
|
model_path, |
|
|
).to(device) |
|
|
|
|
|
def chunk_text_with_max_chunk_size(model, text, tokenizer, prob_threshold=0.5,max_tokens_per_chunk = 400): |
|
|
with torch.no_grad(): |
|
|
|
|
|
# slide context window chunking |
|
|
MAX_TOKENS = 512 |
|
|
tokens = tokenizer(text, return_tensors="pt", truncation=False) |
|
|
input_ids = tokens["input_ids"] |
|
|
attention_mask = tokens["attention_mask"][:, 0:MAX_TOKENS] |
|
|
attention_mask = attention_mask.to(model.device) |
|
|
CLS = input_ids[:, 0].unsqueeze(0) |
|
|
SEP = input_ids[:, -1].unsqueeze(0) |
|
|
input_ids = input_ids[:, 1:-1] |
|
|
model.eval() |
|
|
split_str_poses = [] |
|
|
token_pos = [] |
|
|
windows_start = 0 |
|
|
windows_end = 0 |
|
|
logits_threshold = math.log(1 / prob_threshold - 1) |
|
|
|
|
|
unchunk_tokens = 0 |
|
|
backup_pos = None |
|
|
best_logits = torch.finfo(torch.float32).min |
|
|
STEP = round(((MAX_TOKENS - 2)//2)*1.75 ) |
|
|
print(f"Processing {input_ids.shape[1]} tokens...") |
|
|
# while windows_end <= input_ids.shape[1]:#记得改成windstart |
|
|
while windows_start < input_ids.shape[1]:#记得改成windstart |
|
|
windows_end = windows_start + MAX_TOKENS - 2 |
|
|
ids = torch.cat((CLS, input_ids[:, windows_start:windows_end], SEP), 1) |
|
|
ids = ids.to(model.device) |
|
|
output = model( |
|
|
input_ids=ids, |
|
|
attention_mask=torch.ones(1, ids.shape[1], device=model.device), |
|
|
) |
|
|
logits = output["logits"][:, 1:-1, :] |
|
|
|
|
|
|
|
|
logit_diff = logits[:, :, 1] - logits[:, :, 0] |
|
|
|
|
|
|
|
|
chunk_decision = logit_diff > - logits_threshold |
|
|
greater_rows_indices = torch.where(chunk_decision)[1].tolist() |
|
|
|
|
|
# null or not |
|
|
if len(greater_rows_indices) > 0 and ( |
|
|
not (greater_rows_indices[0] == 0 and len(greater_rows_indices) == 1) |
|
|
): |
|
|
|
|
|
|
|
|
unchunk_tokens_this_window = greater_rows_indices[0] if greater_rows_indices[0]!=0 else greater_rows_indices[1]#exclude the fist index |
|
|
|
|
|
# manually chunk |
|
|
if unchunk_tokens + unchunk_tokens_this_window > max_tokens_per_chunk: |
|
|
big_windows_end = max_tokens_per_chunk - unchunk_tokens |
|
|
max_value, max_index= logit_diff[:,1:big_windows_end].max(), logit_diff[:,1:big_windows_end].argmax() + 1 |
|
|
if best_logits < max_value: |
|
|
backup_pos = windows_start + max_index |
|
|
|
|
|
windows_start = backup_pos |
|
|
|
|
|
|
|
|
split_str_pos = [tokens.token_to_chars(backup_pos + 1).start] |
|
|
split_str_poses = split_str_poses + split_str_pos |
|
|
token_pos = token_pos + [backup_pos] |
|
|
best_logits = torch.finfo(torch.float32).min |
|
|
backup_pos = -1 |
|
|
unchunk_tokens = 0 |
|
|
|
|
|
# auto chunk |
|
|
else: |
|
|
|
|
|
if len(greater_rows_indices) >= 2: |
|
|
for gi, (gri0,gri1) in enumerate(zip(greater_rows_indices[:-1],greater_rows_indices[1:])): |
|
|
|
|
|
if gri1 - gri0 > max_tokens_per_chunk: |
|
|
greater_rows_indices=greater_rows_indices[:gi+1] |
|
|
break |
|
|
|
|
|
split_str_pos = [tokens.token_to_chars(sp + windows_start + 1).start for sp in greater_rows_indices if sp > 0] |
|
|
split_str_poses = split_str_poses + split_str_pos |
|
|
token_pos = token_pos+ [sp + windows_start for sp in greater_rows_indices if sp > 0] |
|
|
|
|
|
windows_start = greater_rows_indices[-1] + windows_start |
|
|
best_logits = torch.finfo(torch.float32).min |
|
|
backup_pos = -1 |
|
|
unchunk_tokens = 0 |
|
|
|
|
|
else: |
|
|
|
|
|
# unchunk_tokens_this_window = min(windows_end - windows_start,STEP) |
|
|
unchunk_tokens_this_window = min(windows_start+STEP,input_ids.shape[1]) - windows_start |
|
|
|
|
|
# manually chunk |
|
|
if unchunk_tokens + unchunk_tokens_this_window > max_tokens_per_chunk: |
|
|
big_windows_end = max_tokens_per_chunk - unchunk_tokens |
|
|
if logit_diff.shape[1] > 1: |
|
|
|
|
|
max_value, max_index= logit_diff[:,1:big_windows_end].max(), logit_diff[:,1:big_windows_end].argmax() + 1 |
|
|
if best_logits < max_value: |
|
|
backup_pos = windows_start + max_index |
|
|
|
|
|
|
|
|
windows_start = backup_pos |
|
|
split_str_pos = [tokens.token_to_chars(backup_pos + 1).start] |
|
|
split_str_poses = split_str_poses + split_str_pos |
|
|
token_pos = token_pos + [backup_pos] |
|
|
best_logits = torch.finfo(torch.float32).min |
|
|
backup_pos = -1 |
|
|
unchunk_tokens = 0 |
|
|
else: |
|
|
# auto leave |
|
|
if logit_diff.shape[1] > 1: |
|
|
max_value, max_index= logit_diff[:,1:].max(), logit_diff[:,1:].argmax() + 1 |
|
|
if best_logits < max_value: |
|
|
best_logits = max_value |
|
|
backup_pos = windows_start + max_index |
|
|
|
|
|
unchunk_tokens = unchunk_tokens + STEP |
|
|
windows_start = windows_start + STEP |
|
|
|
|
|
substrings = [ |
|
|
text[i:j] for i, j in zip([0] + split_str_poses, split_str_poses + [len(text)]) |
|
|
] |
|
|
token_pos = [0] + token_pos |
|
|
return substrings, token_pos |
|
|
# chunking |
|
|
print("\n>>>>>>>>> Chunking...") |
|
|
doc = r'''经典中式家常菜:红烧肉 详尽版食谱 |
|
|
|
|
|
红烧肉,作为中华美食殿堂中一颗璀璨的明珠,以其色泽红亮、肥而不腻、入口即化的独特风味,征服了无数食客的味蕾。它不仅仅是一道菜,更是一种情怀,是记忆中妈妈厨房里飘出的诱人香气,是团圆饭桌上最温暖的慰藉。要烹制出一盘完美的红烧肉,需要耐心、技巧以及对细节的专注。以下将为您详尽分解从选材到成品的每一个步骤,力求让您在家也能复刻出餐厅级别的美味。本食谱旨在深入剖析,故篇幅较长,以确保每个环节都清晰透彻。 |
|
|
|
|
|
一、 精选主料:奠定美味的基石 |
|
|
|
|
|
• 猪肉选择。 |
|
|
|
|
|
◦ 首选带皮的五花肉。 |
|
|
|
|
|
◦ 层次分明为佳。 |
|
|
|
|
|
◦ 肥瘦相间是关键。 |
|
|
|
|
|
◦ 厚度约三指左右。 |
|
|
|
|
|
◦ 重量约一斤半为宜。 |
|
|
|
|
|
◦ 确保猪皮完整无缺。 |
|
|
|
|
|
◦ 新鲜肉品呈鲜红色。 |
|
|
|
|
|
◦ 脂肪部分应洁白细腻。 |
|
|
|
|
|
• 预处理工作。 |
|
|
|
|
|
◦ 用镊子拔除残留猪毛。 |
|
|
|
|
|
◦ 将肉块置于冷水中浸泡。 |
|
|
|
|
|
◦ 浸泡时间约三十分钟。 |
|
|
|
|
|
◦ 中途可换水一至两次。 |
|
|
|
|
|
◦ 目的是泡出部分血水。 |
|
|
|
|
|
◦ 然后用刀刮洗猪皮表面。 |
|
|
|
|
|
◦ 彻底清除污物和杂质。 |
|
|
|
|
|
◦ 最后用流动水冲洗干净。 |
|
|
|
|
|
二、 准备辅料:调配灵魂之味 |
|
|
|
|
|
• 主要调味品。 |
|
|
|
|
|
◦ 老抽:负责上色。 |
|
|
|
|
|
◦ 用量需谨慎控制。 |
|
|
|
|
|
◦ 过多会导致发黑发苦。 |
|
|
|
|
|
◦ 生抽:提供咸鲜底味。 |
|
|
|
|
|
◦ 与老抽比例约二比一。 |
|
|
|
|
|
◦ 优质花雕酒是精髓。 |
|
|
|
|
|
◦ 去腥增香效果显著。 |
|
|
|
|
|
◦ 冰糖:首选黄冰糖。 |
|
|
|
|
|
◦ 炒出的糖色更红亮。 |
|
|
|
|
|
◦ 风味也比白糖醇和。 |
|
|
|
|
|
• 香辛料组合。 |
|
|
|
|
|
◦ 生姜一大块,切片。 |
|
|
|
|
|
◦ 大葱一根,切成长段。 |
|
|
|
|
|
◦ 蒜头数瓣,轻轻拍松。 |
|
|
|
|
|
◦ 八角两至三颗,增香。 |
|
|
|
|
|
◦ 桂皮一小段,勿过多。 |
|
|
|
|
|
◦ 香叶两片,增添风味。 |
|
|
|
|
|
◦ 草果一颗,可拍裂开。 |
|
|
|
|
|
◦ 干辣椒依个人口味添加。 |
|
|
|
|
|
• 其他基础材料。 |
|
|
|
|
|
◦ 食用油适量,需耐高温。 |
|
|
|
|
|
◦ 最好使用菜籽油或花生油。 |
|
|
|
|
|
◦ 食盐少许,用于最后调味。 |
|
|
|
|
|
◦ 因为酱油已有咸度。 |
|
|
|
|
|
◦ 准备足量的开水备用。 |
|
|
|
|
|
◦ 切记不可使用冷水。 |
|
|
|
|
|
◦ 冷水会使肉质收缩变柴。 |
|
|
|
|
|
三、 精细加工:关键步骤解析 |
|
|
|
|
|
• 肉块改刀。 |
|
|
|
|
|
◦ 将洗净的五花肉捞出。 |
|
|
|
|
|
◦ 用厨房纸彻底吸干水分。 |
|
|
|
|
|
◦ 这一步非常重要。 |
|
|
|
|
|
◦ 能有效防止后续溅油。 |
|
|
|
|
|
◦ 将肉切成三厘米见方块。 |
|
|
|
|
|
◦ 大小尽量保持均匀一致。 |
|
|
|
|
|
◦ 以确保受热和入味均匀。 |
|
|
|
|
|
◦ 切面应能看到完美层次。 |
|
|
|
|
|
• 焯水去腥。 |
|
|
|
|
|
◦ 冷水下锅,放入切好的肉。 |
|
|
|
|
|
◦ 同时加入几片生姜。 |
|
|
|
|
|
◦ 倒入一汤匙花雕酒。 |
|
|
|
|
|
◦ 开大火煮沸,撇净浮沫。 |
|
|
|
|
|
◦ 浮沫是血水和杂质所致。 |
|
|
|
|
|
◦ 务必撇除干净直至汤清。 |
|
|
|
|
|
◦ 焯水时间约五到八分钟。 |
|
|
|
|
|
◦ 煮至肉块变色定型即可。 |
|
|
|
|
|
• 捞出与冲洗。 |
|
|
|
|
|
◦ 用漏勺将肉块捞出。 |
|
|
|
|
|
◦ 立即放入温水中冲洗。 |
|
|
|
|
|
◦ 洗去表面残留的浮沫。 |
|
|
|
|
|
◦ 注意水温不宜过低。 |
|
|
|
|
|
◦ 再次用厨房纸吸干水分。 |
|
|
|
|
|
◦ 防止入锅时油花四溅。 |
|
|
|
|
|
◦ 此时肉块呈灰白色。 |
|
|
|
|
|
◦ 经过焯水已无肉腥味。 |
|
|
|
|
|
四、 核心工艺:炒糖色与煸炒 |
|
|
|
|
|
• 炒制糖色。 |
|
|
|
|
|
◦ 锅烧热,倒入少量底油。 |
|
|
|
|
|
◦ 放入准备好的冰糖。 |
|
|
|
|
|
◦ 开中小火慢慢搅动。 |
|
|
|
|
|
◦ 观察冰糖融化的过程。 |
|
|
|
|
|
◦ 先从固体变为液态。 |
|
|
|
|
|
◦ 再从小泡转为密集大泡。 |
|
|
|
|
|
◦ 当大泡逐渐回落消失时。 |
|
|
|
|
|
◦ 糖液颜色开始加深。 |
|
|
|
|
|
• 观察颜色变化。 |
|
|
|
|
|
◦ 从浅黄色变为枣红色。 |
|
|
|
|
|
◦ 这个瞬间非常关键。 |
|
|
|
|
|
◦ 枣红色时立即下入肉块。 |
|
|
|
|
|
◦ 过早则甜腻,过晚则发苦。 |
|
|
|
|
|
◦ 动作务必迅速而准确。 |
|
|
|
|
|
◦ 糖色是红亮色泽的来源。 |
|
|
|
|
|
◦ 也是风味层次的基础。 |
|
|
|
|
|
• 煸炒肉块。 |
|
|
|
|
|
◦ 快速颠锅,使每块肉均匀裹上糖色。 |
|
|
|
|
|
◦ 持续翻炒约三到五分钟。 |
|
|
|
|
|
◦ 直到肉块表面微微焦黄。 |
|
|
|
|
|
◦ 部分油脂被煸炒出来。 |
|
|
|
|
|
◦ 这样吃起来肥而不腻。 |
|
|
|
|
|
◦ 同时香味物质充分释放。 |
|
|
|
|
|
◦ 煸出的猪油可倒出部分。 |
|
|
|
|
|
◦ 留作炒青菜风味极佳。 |
|
|
|
|
|
五、 炖煮入味:时间与火候的艺术 |
|
|
|
|
|
• 加入调料。 |
|
|
|
|
|
◦ 沿着锅边烹入花雕酒。 |
|
|
|
|
|
◦ 瞬间激发出浓郁酒香。 |
|
|
|
|
|
◦ 接着倒入适量生抽。 |
|
|
|
|
|
◦ 再加入少许老抽上色。 |
|
|
|
|
|
◦ 放入所有香料:姜、葱、蒜等。 |
|
|
|
|
|
◦ 与肉块一起翻炒均匀。 |
|
|
|
|
|
◦ 让酱香与肉香充分融合。 |
|
|
|
|
|
◦ 翻炒约两分钟至香气扑鼻。 |
|
|
|
|
|
• 注入开水。 |
|
|
|
|
|
◦ 务必一次性加足开水。 |
|
|
|
|
|
◦ 水量要完全没过肉块。 |
|
|
|
|
|
◦ 甚至可以略多一些。 |
|
|
|
|
|
◦ 避免中途再次加水。 |
|
|
|
|
|
◦ 大火烧开后转小火。 |
|
|
|
|
|
◦ 盖上锅盖,慢火焖炖。 |
|
|
|
|
|
◦ 这是“入口即化”的关键。 |
|
|
|
|
|
◦ 时间至少需要一小时。 |
|
|
|
|
|
• 慢炖过程。 |
|
|
|
|
|
◦ 保持汤面微沸即可。 |
|
|
|
|
|
◦ 火候切忌过大过急。 |
|
|
|
|
|
◦ 否则容易烧干且肉不烂。 |
|
|
|
|
|
◦ 期间可偶尔开盖查看。 |
|
|
|
|
|
◦ 用勺子轻轻推动一下。 |
|
|
|
|
|
◦ 防止粘锅底的情况发生。 |
|
|
|
|
|
◦ 但尽量不要频繁翻动。 |
|
|
|
|
|
◦ 以免影响肉块的完整。 |
|
|
|
|
|
六、 收汁与装盘:成就最终美味 |
|
|
|
|
|
• 大火收汁。 |
|
|
|
|
|
◦ 炖煮一小时后。 |
|
|
|
|
|
◦ 用筷子戳一下瘦肉部分。 |
|
|
|
|
|
◦ 若能轻松戳透即表示已软烂。 |
|
|
|
|
|
◦ 此时根据汤汁咸度加盐。 |
|
|
|
|
|
◦ 开大火,将汤汁收浓。 |
|
|
|
|
|
◦ 用锅铲不停搅动。 |
|
|
|
|
|
◦ 防止糊底,并让汤汁变稠。 |
|
|
|
|
|
◦ 均匀包裹在每一块肉上。 |
|
|
|
|
|
• 收汁技巧。 |
|
|
|
|
|
◦ 收到汤汁浓稠如蜜。 |
|
|
|
|
|
◦ 油亮红润的汤汁紧裹肉块。 |
|
|
|
|
|
◦ 锅中泛起密集的大泡。 |
|
|
|
|
|
◦ 即可准备关火出锅。 |
|
|
|
|
|
◦ 收汁程度依个人喜好。 |
|
|
|
|
|
◦ 喜欢拌饭可多留些汤汁。 |
|
|
|
|
|
◦ 整个过程需密切留意。 |
|
|
|
|
|
◦ 最后阶段变化非常迅速。 |
|
|
|
|
|
• 最终成品。 |
|
|
|
|
|
◦ 将红烧肉盛入预热好的盘中。 |
|
|
|
|
|
◦ 可烫几棵小油菜围边。 |
|
|
|
|
|
◦ 既点缀色彩,又解油腻。 |
|
|
|
|
|
◦ 撒上少许葱花或香菜末。 |
|
|
|
|
|
◦ 一道色香味俱全的红烧肉完成。 |
|
|
|
|
|
◦ 肉质软糯,肥而不腻。 |
|
|
|
|
|
◦ 咸中带甜,回味无穷。 |
|
|
|
|
|
◦ 配上一碗白米饭是绝配。 |
|
|
|
|
|
七、 要点总结与升华 |
|
|
|
|
|
• 成功关键。 |
|
|
|
|
|
◦ 选材是基础,务必新鲜。 |
|
|
|
|
|
◦ 焯水步骤不可省略。 |
|
|
|
|
|
◦ 炒糖色是技术核心。 |
|
|
|
|
|
◦ 火候控制是成败关键。 |
|
|
|
|
|
◦ 耐心慢炖是美味保证。 |
|
|
|
|
|
• 变化与创新。 |
|
|
|
|
|
◦ 可加入土豆、鹌鹑蛋同烧。 |
|
|
|
|
|
◦ 吸收肉汤,滋味更丰富。 |
|
|
|
|
|
◦ 也可尝试用啤酒代替水。 |
|
|
|
|
|
◦ 别有一番风味层次。 |
|
|
|
|
|
◦ 但万变不离其宗。 |
|
|
|
|
|
◦ 核心技法仍需掌握。 |
|
|
|
|
|
烹饪是一门需要实践的艺术,红烧肉更是如此。希望这份详尽的食谱能成为您厨房路上的得力助手,愿您能享受从准备到品尝的整个过程,与家人朋友分享这份由时间与匠心凝聚而成的温暖美味。每一次尝试都是一次经验的积累,祝您烹饪愉快,早日成就属于自己的招牌红烧肉!。 |
|
|
''' |
|
|
# Chunk the text. The prob_threshold should be between (0, 1). The lower it is, the more chunks will be generated. |
|
|
# Therefore adjust it to your need, when prob_threshold is small like 0.000001, each token is one chunk, |
|
|
# when it is set to 1, the whole text will be one chunk, and will be forced to choose a best possible position to chunk when it is about to exceed the max_tokens_per_chunk and no token satisfy the prob_threshold. |
|
|
chunks, token_pos = chunk_text_with_max_chunk_size(model, doc, tokenizer, prob_threshold=0.5, max_tokens_per_chunk = 100) |
|
|
|
|
|
# print chunks |
|
|
for i, (c, t) in enumerate(zip(chunks, token_pos)): |
|
|
print(f"-----chunk: {i}----token_idx: {t}--------") |
|
|
print(c) |
|
|
``` |
|
|
## Citation |
|
|
```bibtex |
|
|
@article{bert-chunker, |
|
|
title={bert-chunker: Efficient and Trained Chunking for Unstructured Documents}, |
|
|
author={Yannan Luo}, |
|
|
year={2024}, |
|
|
url={https://github.com/jackfsuia/bert-chunker} |
|
|
} |
|
|
``` |
|
|
Base model is from [bge-small-zh-v1.5](https://huggingface.co/BAAI/bge-small-zh-v1.5). |
|
|
|