fix: support large input texts with chunking - #100
RSKKSOFFICIAL wants to merge 8 commits into
Conversation
Signed-off-by: RSKKSOFFICIAL <rsksofficial02@gmail.com>
Signed-off-by: RSKKSOFFICIAL <rsksofficial02@gmail.com>
Signed-off-by: RSKKSOFFICIAL <rsksofficial02@gmail.com>
| current_count = 0 | ||
|
|
||
| for sentence in sentences: | ||
| word_count = len(sentence.split()) |
There was a problem hiding this comment.
For Chinese/Japanese/Thai source text, split() returns 1 regardless of length, I think
| self.config["inference"].get("repetition_penalty", 1.0), min_repetition_penalty | ||
| ), | ||
| } | ||
| results = self.translator.translate_batch( |
There was a problem hiding this comment.
Would be nice to use one batch for all chunks, instead of running one batch per chunk in sequence
| logger = logging.getLogger(os.environ["APP_ID"] + __name__) | ||
|
|
||
| # Languages that do not use spaces between words — join chunks without a space separator | ||
| _NO_SPACE_LANGUAGES = {"zh", "ja", "th", "my", "km", "lo", "bo"} |
There was a problem hiding this comment.
What about yue (Cantonese), dz (Dzongkha — same script as the included bo), shn (Shan)?
There was a problem hiding this comment.
Good point. yue, dz, and shn also use writing systems where spaces shouldn't be inserted between chunks. I'll add them to _NO_SPACE_LANGUAGES.
btw, are their any more languages which uses no space?
Signed-off-by: RSKKSOFFICIAL <rsksofficial02@gmail.com>
|
Hi @marcelklehr, I’ve addressed the review comments and tested the changes locally. Everything is working as expected on my side. Could you please take another look and let me know if there’s anything else you’d like me to adjust? |
Signed-off-by: RSKKSOFFICIAL <rsksofficial02@gmail.com>
|
Hello there, We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process. Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6 Thank you for contributing to Nextcloud and we hope to hear from you soon! (If you believe you should not receive this message, you can add yourself to the blocklist.) |
kyteinsky
left a comment
There was a problem hiding this comment.
thanks for the PR, looks good overall but posted some comments maybe we can discuss about.
did you try out translate_iterable? It was mentioned in the original issue: https://opennmt.net/CTranslate2/python/ctranslate2.Translator.html#ctranslate2.Translator.translate_iterable
it seems to have a few upsides like parallel translation (which we don't do here I think) and parallel prefetching, but should simplify the implementation here at least.
| "__comment::chunking": "Options controlling how large inputs are split before translation", | ||
| "__comment::chunking::chunk_threshold": "Word count above which the input is split into chunks before translation", | ||
| "__comment::chunking::chunk_size": "Maximum number of words per chunk when splitting large inputs", | ||
| "__comment::chunking::min_repetition_penalty": "Lower bound for repetition_penalty applied per chunk; overrides inference.repetition_penalty if that value is lower, to suppress runaway output loops on dense scripts such as Devanagari", |
There was a problem hiding this comment.
why add min_repetition_penalty when repetition_penalty is in the same file configurable by the admin?
| # split() always returns 1 for these scripts regardless of actual length. | ||
| text_size = len(cleaned) if is_no_space_source else len(cleaned.split()) | ||
| chunks = ( | ||
| self._chunk_text(cleaned, chunk_size, data.get("origin_language", "")) |
There was a problem hiding this comment.
the origin_language here is always set to "Detect Language" since the model used here does not need it. We could try to add the target languages to the origin language enum but the "Detect Language" option could be selected by the user and we'd have the same sitation when the origin language is not known.
Line 198 in daf679d
It would be better if we do the detection of CJK languages with the existence of space.
| # Devanagari ~1.5x) may need a higher value. Floor of 64 handles very | ||
| # short inputs. | ||
| max_input_tokens = max(len(t) for t in all_input_tokens) | ||
| batch_max_decoding = max(max_input_tokens * max_decoding_multiplier, 64) |
There was a problem hiding this comment.
I was a bit wrong in pushing the max_decoding_length to 10k before, it should be aligned with what the model is trained for, which seems to be 256 for the 3B and the 7B models: (page 34 in https://arxiv.org/pdf/2309.04662)
It should work even with the large lengths but in the previous change we didn't change the input length which is 1024 by default: https://opennmt.net/CTranslate2/python/ctranslate2.Translator.html#ctranslate2.Translator.translate_batch
we can set the max decoding length to 200 to be conservative but even if we cross the 256 limit, the output would "just" be subpar but would work.
the values are in tokens so we would need to think in terms of characters, or maybe translate_iterable can handle that for us.
Co-authored-by: Anupam Kumar <kyteinsky@gmail.com> Signed-off-by: Ravi Shankar Kumar <154051646+RSKKSOFFICIAL@users.noreply.github.com>
Signed-off-by: RSKKSOFFICIAL <rsksofficial02@gmail.com>
for more information, see https://pre-commit.ci
Fixes #71
What this does
Large input texts (>250 words) were being silently truncated by the model
because the output hit the decoding length limit mid-document. This adds a
chunking layer that splits the input into smaller pieces before translation,
then joins the results back into a single output.
Changes
lib/Service.py_chunk_text(): splits input at sentence boundaries into chunks ofmax 80 words. Hard-splits any single sentence that exceeds the limit.
_join_chunks(): joins translated chunks in document order. Uses anempty string separator for no-space languages (zh, ja, th, etc.) and a
single space for all others. Chunk order is always preserved regardless of
source/target script direction — each chunk is already translated correctly
by the model independently.
translate(): applies chunking when input exceeds the threshold,caps
max_decoding_lengthproportionally per chunk to prevent runawayrepetition loops, and enforces a minimum
repetition_penaltyper chunk.config.jsonchunkingsection with four configurable parameters:chunk_threshold(250): word count above which input is chunkedchunk_size(80): max words per chunkmin_repetition_penalty(1.5): lower bound for repetition penalty per chunk, prevents output loops on dense scripts like Devanagarimax_decoding_multiplier(3): output token cap as a multiple of input tokens per chunkTesting
Tested with 350+ word inputs across 7 language pairs:
All pairs now produce complete output covering the full input. Before this
change every pair was truncated at roughly Section 03/04 of a 5-section test
document.
RTL languages (Arabic, Persian) are handled correctly, chunks are always
joined in forward document order since each chunk is translated independently.