vrashad commited on
Commit
ca0e25f
·
verified ·
1 Parent(s): 7a1dada

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +976 -158
README.md CHANGED
@@ -1,199 +1,1017 @@
1
  ---
2
- library_name: transformers
3
- tags: []
 
 
 
 
 
 
 
 
4
  ---
5
 
6
- # Model Card for Model ID
7
 
8
- <!-- Provide a quick summary of what the model is/does. -->
9
 
 
10
 
 
11
 
12
- ## Model Details
 
 
 
 
 
 
 
13
 
14
- ### Model Description
15
-
16
- <!-- Provide a longer summary of what this model is. -->
17
-
18
- This is the model card of a 🤗 transformers model that has been pushed on the Hub. This model card has been automatically generated.
19
-
20
- - **Developed by:** [More Information Needed]
21
- - **Funded by [optional]:** [More Information Needed]
22
- - **Shared by [optional]:** [More Information Needed]
23
- - **Model type:** [More Information Needed]
24
- - **Language(s) (NLP):** [More Information Needed]
25
- - **License:** [More Information Needed]
26
- - **Finetuned from model [optional]:** [More Information Needed]
27
-
28
- ### Model Sources [optional]
29
-
30
- <!-- Provide the basic links for the model. -->
31
-
32
- - **Repository:** [More Information Needed]
33
- - **Paper [optional]:** [More Information Needed]
34
- - **Demo [optional]:** [More Information Needed]
35
-
36
- ## Uses
37
-
38
- <!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->
39
-
40
- ### Direct Use
41
-
42
- <!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->
43
-
44
- [More Information Needed]
45
-
46
- ### Downstream Use [optional]
47
-
48
- <!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->
49
-
50
- [More Information Needed]
51
-
52
- ### Out-of-Scope Use
53
-
54
- <!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->
55
-
56
- [More Information Needed]
57
-
58
- ## Bias, Risks, and Limitations
59
-
60
- <!-- This section is meant to convey both technical and sociotechnical limitations. -->
61
-
62
- [More Information Needed]
63
-
64
- ### Recommendations
65
-
66
- <!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->
67
-
68
- Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.
69
-
70
- ## How to Get Started with the Model
71
-
72
- Use the code below to get started with the model.
73
-
74
- [More Information Needed]
75
-
76
- ## Training Details
77
-
78
- ### Training Data
79
-
80
- <!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->
81
-
82
- [More Information Needed]
83
-
84
- ### Training Procedure
85
-
86
- <!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->
87
-
88
- #### Preprocessing [optional]
89
-
90
- [More Information Needed]
91
-
92
-
93
- #### Training Hyperparameters
94
-
95
- - **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->
96
-
97
- #### Speeds, Sizes, Times [optional]
98
-
99
- <!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->
100
-
101
- [More Information Needed]
102
-
103
- ## Evaluation
104
-
105
- <!-- This section describes the evaluation protocols and provides the results. -->
106
-
107
- ### Testing Data, Factors & Metrics
108
-
109
- #### Testing Data
110
-
111
- <!-- This should link to a Dataset Card if possible. -->
112
-
113
- [More Information Needed]
114
-
115
- #### Factors
116
-
117
- <!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->
118
-
119
- [More Information Needed]
120
-
121
- #### Metrics
122
-
123
- <!-- These are the evaluation metrics being used, ideally with a description of why. -->
124
-
125
- [More Information Needed]
126
-
127
- ### Results
128
-
129
- [More Information Needed]
130
-
131
- #### Summary
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
 
133
 
 
134
 
135
- ## Model Examination [optional]
136
 
137
- <!-- Relevant interpretability work for the model goes here -->
 
 
 
 
138
 
139
- [More Information Needed]
140
 
141
- ## Environmental Impact
 
 
 
 
142
 
143
- <!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->
144
 
145
- Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).
 
 
 
 
146
 
147
- - **Hardware Type:** [More Information Needed]
148
- - **Hours used:** [More Information Needed]
149
- - **Cloud Provider:** [More Information Needed]
150
- - **Compute Region:** [More Information Needed]
151
- - **Carbon Emitted:** [More Information Needed]
152
 
153
- ## Technical Specifications [optional]
154
 
155
- ### Model Architecture and Objective
 
 
 
 
 
156
 
157
- [More Information Needed]
158
 
159
- ### Compute Infrastructure
160
 
161
- [More Information Needed]
 
 
 
162
 
163
- #### Hardware
164
 
165
- [More Information Needed]
166
 
167
- #### Software
168
 
169
- [More Information Needed]
 
 
 
170
 
171
- ## Citation [optional]
 
 
 
172
 
173
- <!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->
 
 
 
174
 
175
- **BibTeX:**
176
 
177
- [More Information Needed]
178
 
179
- **APA:**
 
 
 
180
 
181
- [More Information Needed]
 
 
 
182
 
183
- ## Glossary [optional]
 
 
 
184
 
185
- <!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->
186
 
187
- [More Information Needed]
188
 
189
- ## More Information [optional]
190
 
191
- [More Information Needed]
192
 
193
- ## Model Card Authors [optional]
194
 
195
- [More Information Needed]
196
 
197
- ## Model Card Contact
198
 
199
- [More Information Needed]
 
1
  ---
2
+ license: cc-by-4.0
3
+ language:
4
+ - az
5
+ base_model:
6
+ - FacebookAI/xlm-roberta-base
7
+ pipeline_tag: text-classification
8
+ tags:
9
+ - rag
10
+ - chunker
11
+ - semantic
12
  ---
13
 
14
+ # Azerbaijani Semantic Text Chunker
15
 
16
+ Advanced semantic text chunking model for Azerbaijani language based on XLM-RoBERTa
17
 
18
+ This model specifically trained for Azerbaijani texts, featuring semantic understanding, strict length control, and sliding window processing for documents of any size.
19
 
20
+ ## Features
21
 
22
+ - **Semantic Chunking**: Understands context and meaning, not just sentence boundaries
23
+ - **Azerbaijani Specialized**: Native support for Azerbaijani language nuances
24
+ - **Strict Length Control**: Enforces maximum chunk size in tokens and/or characters
25
+ - **Sliding Window Processing**: Handles documents of unlimited length (2K, 10K, 50K+ tokens)
26
+ - **Multiple Strategies**: Optimal, conservative, and aggressive chunking modes
27
+ - **Flexible Limits**: Supports token limits, character limits, or both combined
28
+ - **Detailed Analytics**: Comprehensive chunking process diagnostics
29
+ - **RAG-Ready**: Perfect for vector databases and retrieval systems
30
 
31
+ ## Performance
32
+
33
+ Our model achieves excellent results on Azerbaijani text segmentation:
34
+
35
+ | Metric | Score |
36
+ |--------|-------|
37
+ | **Precision** | 0.7850 |
38
+ | **Recall** | 0.6485 |
39
+ | **F1-Score** | 0.7102 |
40
+ | **Accuracy** | 0.9980 |
41
+
42
+
43
+
44
+ ## Technical Architecture
45
+
46
+
47
+
48
+ ### Sliding Window Processing
49
+ The model uses an advanced sliding window approach to handle texts longer than the 512-token context window:
50
+
51
+ Text Length: 2048+ tokens
52
+ Window Size: 510 tokens (512 - 2 special tokens)
53
+ Stride: 255 tokens (50% overlap)
54
+
55
+ **Window 1**: tokens 0-510
56
+ **Window 2**: tokens 255-765 (255 token overlap)
57
+ **Window 3**: tokens 510-1020 (255 token overlap)
58
+ **Window 4**: tokens 765-1275 (255 token overlap)
59
+ **Window 5**: tokens 1020-1530 (255 token overlap)
60
+ **Window 6**: tokens 1275-1785 (255 token overlap)
61
+ **Window 7**: tokens 1530-2040 (255 token overlap)
62
+ **Window 8**: tokens 1785-2048 (final window)
63
+
64
+
65
+ **Key advantages:**
66
+ - **No length limitations**: Process documents of any size
67
+ - **Intelligent overlap**: Ensures no semantic boundaries are missed
68
+ - **Prediction fusion**: Combines results using maximum confidence scores
69
+ - **Linear complexity**: O(N) processing time regardless of document length
70
+
71
+ ### Length Control Algorithm
72
+ - **Strict enforcement**: Never exceeds specified token/character limits
73
+ - **Smart boundary detection**: Prefers natural separators (spaces, punctuation)
74
+ - **Fallback mechanisms**: Intelligent splitting when no semantic boundaries found
75
+ - **Combined limits**: Supports both token AND character limits simultaneously
76
+
77
+ ## Quick Start
78
+
79
+ ### Installation
80
+
81
+ ```bash
82
+ # Install requirements
83
+ pip install torch transformers numpy pandas
84
+ ```
85
+
86
+ ### Basic Usage
87
+
88
+ ```python
89
+ import torch
90
+ import torch.nn.functional as F
91
+ from transformers import AutoTokenizer, AutoModelForTokenClassification
92
+ import numpy as np
93
+ from typing import List, Optional, Tuple
94
+
95
+ # Global priority constants
96
+ PRIORITY_TOKENS = "tokens"
97
+ PRIORITY_CHARS = "chars"
98
+ PRIORITY_STRICT = "strict"
99
+
100
+ # Global configuration variables for easy modification
101
+ MODEL_PATH = "LocalDoc/semantic_chunker"
102
+ MAX_CHUNK_TOKENS = 400 # Single parameter for chunk size
103
+ TARGET_TOKENS = 256 # Single parameter for target size
104
+ MAX_CHUNK_CHARS = None # No character limit by default
105
+ THRESHOLD = 0.12
106
+ PRIORITY = PRIORITY_STRICT
107
+
108
+ # Dynamic calculation constants (avoid hardcoding)
109
+ MIN_LENGTH_DIVISOR = 8 # target_tokens // MIN_LENGTH_DIVISOR for min chunk length
110
+ MARGIN_RATIO = 0.25 # 25% margin for max_tokens validation
111
+ MIN_MARGIN = 20 # Minimum margin when validating max_tokens
112
+ ROUGH_TOKEN_MULTIPLIER = 1.3 # Rough token estimation multiplier
113
+ LONG_TEXT_THRESHOLD = 400 # Threshold for long text processing
114
+ BERT_MAX_LENGTH = 512 # Standard BERT model limit
115
+ SEARCH_RANGE = 50 # Character search range for boundaries
116
+ SENTENCE_SEARCH_RANGE = 100 # Range for sentence boundary search
117
+ SPACE_SEARCH_RANGE = 50 # Range for space boundary search
118
+ OPTIMAL_TARGET_RATIO = 0.9 # 90% of max as safety margin for merging
119
+ GOOD_SIZE_MIN_RATIO = 0.7 # 70% of target for minimum good size
120
+ MERGE_THRESHOLD_RATIO = 0.5 # 50% of target for merge threshold
121
+ MERGE_ATTEMPT_RATIO = 0.6 # 60% of target for merge attempts
122
+ OPTIMAL_MAX_RATIO = 1.1 # 110% of max for slight overflow
123
+ MIN_ACCEPTABLE_RATIO = 0.3 # 30% of target for minimum acceptable size
124
+ MAX_MERGE_RATIO = 1.5 # 150% of target for maximum merge size
125
+
126
+ # Sample text for testing
127
+ #SAMPLE_TEXT = """Azərbaycan, Qafqazın incisi olaraq, zəngin tarixi, unikal mədəniyyəti və möhtəşəm təbiəti ilə hər zaman diqqət çəkmişdir. Bu torpaqlar minillər boyu müxtəlif sivilizasiyaların qovuşağında yerləşmiş, onların izlərini özündə yaşatmışdır. Şərqlə Qərb arasında bir körpü rolunu oynayan Azərbaycan, özünün çoxəsrlik dövlətçilik ənənəsi ilə fəxr edir. Ölkənin paytaxtı Bakı, Xəzər dənizinin sahilində yerləşən, Qədim Şəhər (İçərişəhər) kimi UNESCO Dünya İrs Siyahısına daxil edilmiş tarixi məkanları və müasir memarlıq abidələri ilə bir araya gətirən canlı bir metropoldur. Müasir Bakının simvollarından olan Heydər Əliyev Mərkəzi, incəsənət, mədəniyyət və təhsilin mərkəzi kimi fəaliyyət göstərir, onun innovativ dizaynı ilə dünyanın diqqətini cəlb edir. Azərbaycanın təbiəti də heyrətamiz dərəcədə müxtəlifdir. Böyük Qafqaz dağlarının əzəməti, Kiçik Qafqazın mənzərəli vadiləri, Kür-Araz ovalığının bərəkəti və Xəzər dənizinin sahil zolağı ölkəyə özünəməxsus gözəllik qatır. Qax rayonundakı Laza kəndi, dağların əhatəsində yerləşən, təbii gözəlliyi ilə seçilən bir turizm mərkəzidir. Bu ərazilərdə füsunkar meşələr, bol sulu çaylar, şəlalələr və müalicəvi mineral bulaqlar mövcuddur. Göygöl Milli Parkı, Azərbaycanın ən gözəl təbii guşələrindən biridir, onun saf gölü və ətrafındakı meşələr, nadir bitki və heyvan növlərinə ev sahibliyi edir. Texnologiyanın sürətli inkişafı dövründə Azərbaycan da bu qlobal trenddən kənarda qalmayıb. Son illərdə ölkədə rəqəmsal transformasiya prosesləri sürətlənmiş, informasiya-kommunikasiya texnologiyaları (İKT) sektoruna böyük investisiyalar qoyulmuşdur. Bakıda "Hi-Tech Park" kimi müasir texnoloji mərkəzlər fəaliyyət göstərir, innovativ startaplara dəstək verilir. Süni intellekt (AI), maşın təlimi (ML) və böyük verilənlər (Big Data) kimi sahələr prioritet istiqamətlər olaraq müəyyən edilmişdir. Ölkənin gələcəyi üçün əsaslı önəm kəsb edən bu sahələr, təhsil və elmi araşdırmalarla sıx əlaqədardır. Universitetlərdə və elmi institutlarda İKT-nin müxtəlif istiqamətləri üzrə tədqiqatlar aparılır, gənc kadrlar hazırlanır. Virtual və artırılmış reallıq texnologiyaları da təhsil, mədəniyyət və turizm sahələrində tətbiq olunmağa başlanmışdır. "Azercosmos" ASC, peyk texnologiyaları sahəsində ölkənin potensialını artırır, bu da telekommunikasiya, naviqasiya və yerin uzaqdan müşahidəsi kimi sahələrdə əhəmiyyətli rol oynayır. Azərbaycanın tarixi boyu bir çox mühüm hadisələrə şahidlik etmişdir. Qədim zamanlarda Albaniya dövlətinin yaranması, İpək Yolunun bir hissəsinin bu ərazidən keçməsi, orta əsrlərdə Şirvanşahlar və Səfəvilər kimi güclü dövlətlərin hökmranlığı ölkənin siyasi və mədəni inkişafına təsir göstərmişdir. 1918-ci ildə Şərqdə ilk demokratik respublika olan Azərbaycan Xalq Cümhuriyyətinin qurulması, bu torpaqların müstəqillik uğrunda mübarizəsinin parlaq nümunəsidir. Sovet hakimiyyəti illərindəki çətinliklərə baxmayaraq, Azərbaycan 1991-ci ildə yenidən müstəqilliyini qazanmışdır. Ulu Öndər Heydər Əliyevin müstəqil Azərbaycanın təməlini qoyması və Prezident İlham Əliyevin rəhbərliyi ilə ölkənin inkişaf etməsi, bu günümüzdə də davam edən uğurlu siyasətin nəticəsidir. Qarabag müharibəsi və 2020-ci ildəki Vətən Müharibəsi, Azərbaycanın ərazi bütövlüyünün təmin edilməsində həlledici rol oynamışdır. Bu qələbələr, xalqımızın birliyini və gücünü bir daha təsdiqlədi. Gələcəyə baxış, Azərbaycan üçün böyük potensial vəd edir. Ölkə, enerji resursları baxımından zəngin olmaqla yanaşı, tranzit və logistika mərkəzi kimi də strateji əhəmiyyət kəsb edir. "Asrın Müqaviləsi"nin imzalanması, Xəzər dənizinin neft və qaz yataqlarının dünya bazarına çıxarılmasında mühüm rol oynamışdır. "Şərq-Qərb" və "Şimal-Cənub" beynəlxalq nəqliyyat dəhlizlərinin inkişafı, Azərbaycanın regionda aparıcı logistika mərkəzinə çevrilməsinə şərait yaradır. Yaşıl enerji və dayanıqlı inkişaf konsepsiyaları da ölkənin gələcək strategiyasının əsasını təşkil edir. Günəş və külək enerjisinin inkişafına böyük diqqət yetirilir, iqlim dəyişikliyinin təsirlərini azaltmaq üçün səylər artırılır. Təhsil sisteminin təkmilləşdirilməsi, gənclərin beynəlxalq standartlara uyğun kadr kimi yetişdirilməsi, elmi-texniki potensialın gücləndirilməsi gələcək uğurların təminatıdır. Şəhərlərin yenidən qurulması, infrastrukturun modernləşdirilməsi, sosial sahələrin inkişafı, bütün bunlar ölkənin gələcək tərəqqisi üçün atılan addımlardır. Azərbaycan, Avropa ilə inteqrasiya, beynəlxalq əməkdaşlığın gücləndirilməsi və regional sabitliyin təmin edilməsi istiqamətində də aktiv fəaliyyət göstərir. Sülh və əməkdaşlıq siyasəti, Azərbaycanın beynəlxalq aləmdəki mövqeyini daha da gücləndirir. İnformasiya Texnologiyaları Universiteti Azərbaycan Respublikasında informasiya cəmiyyəti quruculuğu istiqaməti üzrə yüksək hazırlığa malik kadr potensialının formalaşdırılmasını təmin edən ali təhsil müəssisəsi idi. Azərbaycan Respublikasının Prezidentinin Sərəncamı ilə 1 fevral 2013-cü ildə yaradılmışdır.[2] İnformasiya Texnologiyaları Universitetinin və Azərbaycan Respublikası Xarici İşlər Nazirliyinin Diplomatik Akademiyasının əsasında, 13 yanvar 2014-cü ildə "ADA" Universiteti yaradılması ilə fəaliyyəti başa çatmışdır.[3]Universitetin tarixi 2006-cı ildən başlayır. Belə ki, 2006-cı ilin martında Azərbaycan Xarici İşlər Nazirliyinin nəzdində Azərbaycan Diplomatik Akademiyası adı altında ali təhsil müəssisəsi yaradılmışdır. Azərbaycan Respublikası Prezidentinin 13 yanvar 2014-cü il tarixli Sərəncamı ilə Azərbaycan Respublikası Xarici İşlər Nazirliyinin Diplomatik Akademiyasının və Azərbaycan Respublikasında İnformasiya Texnologiyaları Universitetinin əsasında "ADA" Universiteti yaradılmışdır[4].Əsas məqsədi diplomatiya, ictimai münasibətlər, biznes, informasiya texnologiyaları və sistem mühəndisliyi üzrə qlobal liderlər hazırlamaqdır. Akademiyanın əsasını qoyan rektor, Azərbaycan Xarici İşlər Nazirinin müavini və Azərbaycanın ABŞ-dəki sabiq səfiri Hafiz Paşayevdir.[5]2012-ci ildə Bakı şəhərində "Dədə Qorqud" parkı yaxınlığında yerləşən yeni "Green and Smart" kampusuna köçmüşdür. 2009-cu ildən magistr pilləsi, 2011-ci ildən bakalavr pilləsində ali təhsil verir. XIV əsrdə Rəşidəddin Fəzlullah ibn Əbil-Xeyrə Əli Həmədaninin qələmə aldığı Cəmi ət-Təvarix (Tarix toplusu) adlı əsərinin "Mujallad-i Awwal" (Birinci Kitabı: Monqol tarixi)in "Bab-i Awwal" (Birinci Bölüm: Türk ve Monqol qəbilələrinin tarixi)ində monqolların yaradılış dastanı olaraq qeyd edilmiş əfsanə,[5][6][7] 17. yüzildə Şibanın nəvələrindən və Xivə xanlığının xanı olan Əbulqazi Bahadır xanın qələmə aldığı Şəcərəyi Türk adlı əsərdə də monqolların yaradılış dastanı olaraq qeyd edilmişdir, lakin bəzi mənbələrə görə də Türk dastanıdır.[6][7] Bəhsi keçən hər iki tarixi mənbədə Nekuz (Nüküz) və Qiyan (Kıyan) adlı qardaşlar ilə xanımları tatarlar tərəfindən məğlub edildikdən sonra Ərgənəqon (Farsca:ارگنه قون; Ergene Qon) adı verilən dar və sıldırım bir yerə getmiş, 400 ildə sülaləsi çoxalıb Ərgənəqondan çıxmşdır. Ərgənəqondan çıxdıqları zaman yol göstərənin Börteçine olduğu düşünülməkdədir.[7]Ancaq Göytürklərin diriliş dastanı ilə olan oxşarlıqları səbəb göstərərək Türklərə aid bir dastan olduğunu iddia edən tədqiqatçılar da var.[7][8] Talat Sait Halman isə mifoloji bir varlıq olan Bozqurdun müdafiəsi sayəsində soyunun tükənmə təhlükəsindən qurtulan və yenə Bozqurtlar sayəsində dağlarla əhatə olunmuş Ərgənəqon vadisindən çıxan bir Türk toplumunun hekayəsindən bəhs edildiyini iddia edir.[9] Digər görüşlərə görə isə Türklər və monqollar arasında bənzər olan əfsanələr vardır.[10] Əfsanə bəzən də Novruz ilə əlaqələndirilir.[11]"""
128
+ SAMPLE_TEXT = """Azərbaycan Respublikası, Qafqaz regionunda yerləşən, unikal coğrafi mövqeyi, zəngin tarixi və çoxşaxəli mədəniyyəti ilə hər zaman özünəməxsus yer tutmuşdur. Bu torpaqlar minillər ərzində müxtəlif sivilizasiyaların, dövlətlərin və mədəniyyətlərin təsirinə məruz qalmış, özündə dərin izlər buraxmışdır. Şərqlə Qərb arasında körpü rolunu oynayan Azərbaycan, Şirvanşahlar, Səfəvilər, Qaraqoyunlular, Ağqoyunlular kimi güclü dövlətlərin mərkəzi olmuş, İpək Yolu üzərində strateji əhəmiyyət kəsb etmişdir. Müstəqilliyini bərpa etdikdən sonra, xüsusilə son illərdə, ölkə dinamik inkişaf yolu keçərək regionda aparıcı dövlətlərdən birinə çevrilmişdir. Bakı şəhəri, Azərbaycanın paytaxtı olaraq, Xəzər dənizinin sahilində yerləşən, qədimlik və müasirlik vəhdətini özündə yaşadan möhtəşəm bir məkandır. UNESCO Dünya İrs Siyahısına daxil edilmiş Qədim Şəhər (İçərişəhər) kompleksi, orta əsr memarlığının nadir nümunələrini özündə əks etdirir. Şirvanşahlar Sarayı, Qız Qalası kimi tarixi abidələr buranın qədim tarixindən xəbər verir. Eyni zamanda, Heydər Əliyev Mərkəzi, Alov Qüllələri, Bakı Abadlıq Kompleksi kimi müasir memarlıq inciləri şəhərə müasir və unikal görkəm qatır. Bu müasir tikililər, innovativ dizaynları və texnoloji həlləri ilə diqqət çəkir. Azərbaycanın təbiəti də onun mədəni zənginliyi qədər heyranedici və müxtəlifdir. Ölkə ərazisi, Böyük Qafqaz dağ sisteminin cənub-şərq yamaclarından Kiçik Qafqaz silsiləsinə, Kür-Araz ovalığından Lənkəran ovalığına qədər geniş diapazonda müxtəlif relyef formalarını əhatə edir. Bu coğrafi müxtəliflik, müxtəlif iqlim tiplərinin və zəngin biomüxtəlifliyin yaranmasına səbəb olmuşdur. Qəbələ, Şəki, Qax kimi şimal rayonları dağlıq və meşəlik əraziləri, bol bulaqları, ecazkar mənzərələri ilə tanınır. Lənkəran və Astara rayonları subtropik iqlimə malik olub, rütubətli meşələri və unikal flora və faunası ilə seçilir. Göygöl Milli Parkı, Şahdağ Milli Parkı kimi qorunan ərazilər, ölkənin təbiətini qorumaq, həm də elmi tədqiqatlar aparmaq üçün əhəmiyyətli mərkəzlərdir. Göygölün özü, onun ətrafındakı meşələr və dağlar, həm də bir çox əfsanə və rəvayətlərə məskən olmuşdur. Elm və texnologiya sahəsində Azərbaycanın nailiyyətləri də xüsusi qeyd olunmalıdır. Ölkədə İKT sektorunun inkişafına böyük önəm verilir. Rəqəmsal transformasiya, süni intellekt (AI), maşın təlimi (ML), böyük verilənlər (Big Data) texnologiyalarının tətbiqi prioritet istiqamətlərdir. Bakıda fəaliyyət göstərən "Hi-Tech Park" və "Sumqayıt Kimya Sənaye Parkı" kimi texnoloji və sənaye parkları, innovativ layihələrin həyata keçirilməsi, yeni texnologiyaların yaradılması və tətbiqi üçün əlverişli mühit yaradır. "Azercosmos" Açıq Səhmdar Cəmiyyəti, peyk texnologiyaları sahəsində ölkənin potensialını artıraraq, telekommunikasiya, naviqasiya, telekommunikasiya, yerin uzaqdan müşahidəsi və informasiya təhlükəsizliyi kimi sahələrdə mühüm rol oynayır. Universitetlərdə, xüsusilə də ADA Universiteti, İnformasiya Texnologiyaları Universiteti (indiki ADA Universitetinin tərkibində) kimi təhsil müəssisələrində İKT sahələri üzrə tədqiqatlar aparılır, yüksək ixtisaslı kadrlar hazırlanır. Virtual və artırılmış reallıq texnologiyaları da təhsil, mədəniyyət və turizm sahələrində geniş tətbiq olunur. Bu sahələrin inkişafı ölkənin gələcək dayanıqlı inkişafı üçün əsasdır. Azərbaycan tarixi boyu bir çox mühüm hadisələrə şahidlik etmişdir. Qədim dövrlərdə Midiya, Atropatena, Qafqaz Albaniyası kimi dövlətlərin yaranması, sonrakı dövrlərdə Arran, Şirvan, Gəncə xanlıqlarının mövcudiyyəti ölkənin dövlətçilik ənənəsini gücləndirmişdir. 1918-ci ildə Şərqdə ilk demokratik respublika olan Azərbaycan Xalq Cümhuriyyətinin qurulması, bu torpaqların azadlıqsevərliyinin və müstəqillik uğrunda mübarizəsinin parlaq bir nümunəsidir. 1920-ci ildə Aprel işğalından sonra Sovet hakimiyyəti qurulsa da, xalq heç vaxt öz milli kimliyindən və dövlətçilik arzularından vaz keçməmişdir. 1991-ci ildə Ümummilli Lider Heydər Əliyevin müdrik siyasəti və xalqın iradəsi sayəsində Azərbaycan yenidən müstəqilliyini qazanmışdır. Sonrakı illərdə, Prezident İlham Əliyevin rəhbərliyi altında ölkə sosial-iqtisadi, siyasi və hərbi sahələrdə böyük nailiyyətlər əldə etmişdir. 2020-ci ildə aparılan Vətən Müharibəsi nəticəsində ölkənin ərazi bütövlüyü tam təmin olunmuş, doğma torpaqlarımız 30 illik işğaldan azad edilmişdir. Bu qələbə, Azərbaycan xalqının birliyinin, gücünün və iradəsini bütün dünyaya bir daha sübut etmişdir. Azərbaycanın gələcəkə baxışı, həm ölkə daxilində, həm də beynəlxalq aləmdə özünü göstərir. Ölkə, enerji resursları (neft və qaz) ixracatçısı olmaqla yanaşı, yeni tranzit və logistika mərkəzi kimi də strateji əhəmiyyətini artırmışdır. "Asrın Müqaviləsi" və sonrakı neft-qaz layihələri ölkə iqtisadiyyatının inkişafına böyük təkan vermişdir. Bakı-Tbilisi-Qars dəmir yolu, "Şərq-Qərb" və "Şimal-Cənub" beynəlxalq nəqliyyat dəhlizlərinin mühüm hissəsi kimi, Azərbaycanın Avrasiya məkanında rolunu gücləndirmişdir. Yaşıl enerji və dayanıqlı inkişaf konsepsiyaları da ölkənin gələcək strategiyasının əsasını təşkil edir. Günəş və külək enerjisi potensialından səmərəli istifadə etmək, emissiyaları azaltmaq və iqlim dəyişikliyinin mənfi təsirlərini minimuma endirmək üçün ciddi səylər göstərilir. Təhsil sisteminin modernləşdirilməsi, gənclərin beynəlxalq səviyyədə rəqabətədavamlı kadr kimi yetişdirilməsi, elmi-texniki potensialın artırılması ölkənin gələcək uğurları üçün prioritetdir. Şəhərlərin, xüsusilə də regionların sosial-iqtisadi inkişafına yönəlmiş dövlət proqramları, infrastruktur layihələri, sosial təminatın gücləndirilməsi, bütün bunlar Azərbaycanın davamlı tərəqqisinin təminatıdır. Beynəlxalq əməkdaşlığın genişləndirilməsi, Avropa İttifaqı və digər beynəlxalq təşkilatlarla əlaqələrin möhkəmləndirilməsi, regional sabitliyin və təhlükəsizliyin təmin edilməsində aktiv rol oynamaq, Azərbaycanın xarici siyasətinin əsas istiqamətləridir. Sülhə və əməkdaşlığa əsaslanan bu siyasət, ölkənin beynəlxalq aləmdəki nüfuzunu daha da artırır. Tarixi və mədəni irsimizin qorunması da dövlətimizin prioritetlərindəndir. Naxçıvan Muxtar Respublikasında yerləşən Əshabi-Kəhf, Şəki Xan Sarayı, Qobustan qaya təsvirləri, Qarabağın mədəniyyət abidələri, milli musiqimizin (muğam) UNESCO tərəfindən qeyri-maddi mədəni irs siyahısına daxil edilməsi, Azərbaycanın zəngin mədəniyyətini dünyaya tanıtmaq istiqamətində atılan mühüm addımlardır. Qarabağın azad olunmasından sonra, işğaldan ziyan dəymiş mədəniyyət obyektlərinin, dini məbədlərin, xüsusilə də Şuşa şəhərinin bərpası və qorunması işlərinə başlanılmışdır. Şuşa, Azərbaycanın mədəniyyət paytaxtı elan edilmişdir və bu, onun milli-mənəvi dəyərlər sistemimizdəki xüsusi yerini bir daha təsdiqləyir. Arxeoloji qazıntılar, tarixi abidələrin restavrasiyası, muzeylərin fəaliyyətinin təkmilləşdirilməsi, yeni mədəniyyət mərkəzlərinin yaradılması, gənclərin milli mədəniyyətimizə marağının artırılması istiqamətində davamlı işlər aparılır. Bu işlər, gələcək nəsillərə örnək olaraq, ölkəmizin unikal mədəni irsinin qorunub saxlanılmasına və təbliğinə xidmət edir. Elmi tədqiqatlar sahəsində də böyük irəliləyişlər müşahidə olunur. Milli Elmlər Akademiyası (AMEA) və müxtəlif ali təhsil müəssisələrinin alimləri, fundamental və tətbiqi elmlərin müxtəlif sahələrində mühüm nailiyyətlər əldə edirlər. Fizika, kimya, biologiya, texnika elmləri, humanitar və sosial elmlər sahələrində aparılan tədqiqatlar, ölkənin elmi potensialını artırmaqla yanaşı, həm də qlobal elmi proseslərə töhfə verir. Xüsusilə nanotexnologiyalar, materialşünaslıq, süni intellekt, bioteknologiya, ekologiya və yer elmləri kimi müasir istiqamətlərdə aparılan tədqiqatlar, prioritet sahələr olaraq müəyyən edilmişdir. Gənc alimlərin hazırlanması, elmi-tədqiqat işlərinə cəlb edilməsi, beynəlxalq elmi əməkdaşlığın gücləndirilməsi, elmi jurnal və konfransların təşkili, müasir innovativ ideyaların reallaşdırılması üçün qrant proqramlarının maliyyələşdirilməsi, bütün bunlar elmin inkişafına dövlət qayğısının bariz nümunələridir. Akademik təqaüdlərin verilməsi, elmi müəssisələrin maddi-texniki bazasının gücləndirilməsi, beynəlxalq reytinqli jurnallarda məqalələrin dərc olunması üçün şərait yaradılması, həmçinin elmi nəticələrin sənaye və iqtisadiyyatla inteqrasiyası, ölkənin elmi-texniki potensialını gücləndirir. Tarixi Azərbaycan torpaqları, həm də zəngin folklora, ədəbiyyata və incəsənətə malikdir. Dədə Qorqud dastanları, Nizami Gəncəvinin, Füzulinin, Vahid, Nəsimi kimi dahi şairlərin əsərləri, Azərbaycan ədəbiyyatının qızıl fondunu təşkil edir. Muğam ifaçılıq sənəti, xalçaçılıq, metal üzərində işləmə sənəti, memarlıq məktəbləri, milli rəqs və musiqi janrları – bütün bunlar Azərbaycanın dünya mədəniyyətinə verdiyi töhfələrdir. Bu dəyərlərin qorunması, təbliği və gələcək nəsillərə ötürülməsi, dövlətimizin qarşısında duran mühüm vəzifələrdəndir. Mədəniyyət Nazirliyinin fəaliyyəti, muzeylərin, teatrların, musiqi kollektivlərinin işinin təkmilləşdirilməsi, xarici ölkələrdə Azərbaycan mədəniyyətinin təbliğ edilməsi, müxtəlif mədəni tədbirlərin – festivalların, sərgilərin, konsertlərin – təşkili, bütün bunlar ölkəmizin mədəni həyatının zənginliyini göstərir."""
129
+
130
+ class AzerbaijaniTextChunker:
131
+ """Optimized Azerbaijani text chunker with single parameter configuration"""
132
+
133
+ def __init__(self, model_path: str = MODEL_PATH):
134
+ self.model_path = model_path
135
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
136
+ self.tokenizer = None
137
+ self.model = None
138
+ self.is_loaded = False
139
+
140
+ def load_model(self) -> bool:
141
+ """Load the trained model"""
142
+ try:
143
+ self.tokenizer = AutoTokenizer.from_pretrained(
144
+ self.model_path, use_fast=True, trust_remote_code=False
145
+ )
146
+ self.model = AutoModelForTokenClassification.from_pretrained(
147
+ self.model_path, trust_remote_code=False
148
+ )
149
+ self.model.to(self.device)
150
+ self.model.eval()
151
+ self.is_loaded = True
152
+ return True
153
+ except Exception as e:
154
+ print(f"Error loading model: {e}")
155
+ return False
156
+
157
+ def chunk(self,
158
+ text: str,
159
+ max_chunk_tokens: Optional[int] = None,
160
+ target_tokens: Optional[int] = None,
161
+ max_chunk_chars: Optional[int] = None,
162
+ threshold: float = None,
163
+ priority: str = None) -> List[str]:
164
+ """
165
+ Chunk text into semantic segments with optimal sizing
166
+
167
+ Args:
168
+ text: Input text to chunk
169
+ max_chunk_tokens: Maximum tokens per chunk (default: from global config)
170
+ target_tokens: Target optimal size in tokens (default: from global config)
171
+ max_chunk_chars: Maximum characters per chunk (default: from global config)
172
+ threshold: Confidence threshold for splitting (default: from global config)
173
+ priority: Which limit to prioritize when both are set (default: from global config)
174
+
175
+ Returns:
176
+ List of optimally-sized text chunks
177
+ """
178
+ if not self.is_loaded:
179
+ if not self.load_model():
180
+ return [text]
181
+
182
+ # Use global configuration as defaults
183
+ if max_chunk_tokens is None:
184
+ max_chunk_tokens = MAX_CHUNK_TOKENS
185
+ if target_tokens is None:
186
+ target_tokens = TARGET_TOKENS
187
+ if max_chunk_chars is None:
188
+ max_chunk_chars = MAX_CHUNK_CHARS
189
+ if threshold is None:
190
+ threshold = THRESHOLD
191
+ if priority is None:
192
+ priority = PRIORITY
193
+
194
+ # Dynamic validation and minimum length calculation
195
+ dynamic_min_length = max(10, target_tokens // MIN_LENGTH_DIVISOR)
196
+
197
+ if not text or len(text.strip()) < dynamic_min_length:
198
+ return [text.strip()] if text.strip() else []
199
+
200
+ text = text.strip()
201
+
202
+ # Ensure max >= target with reasonable margin
203
+ if max_chunk_tokens < target_tokens:
204
+ margin = max(target_tokens * MARGIN_RATIO, MIN_MARGIN)
205
+ max_chunk_tokens = target_tokens + int(margin)
206
+
207
+ # Step 1: Get all semantic boundaries from the model
208
+ semantic_boundaries = self._get_semantic_boundaries_fixed(text, threshold)
209
+
210
+ # Step 2: Split text at semantic boundaries (no limits yet)
211
+ initial_chunks = self._split_at_boundaries(text, semantic_boundaries)
212
+
213
+ # Step 3: Optimize chunk sizes with dynamic parameters
214
+ optimized_chunks = self._optimize_chunk_sizes(
215
+ initial_chunks, max_chunk_tokens, target_tokens
216
+ )
217
+
218
+ # Step 4: Apply additional limits if specified
219
+ if max_chunk_chars is not None:
220
+ final_chunks = self._apply_char_limits(optimized_chunks, max_chunk_chars)
221
+ else:
222
+ final_chunks = optimized_chunks
223
+
224
+ return self._clean_and_validate_chunks(final_chunks, target_tokens, max_chunk_tokens)
225
+
226
+ def _get_semantic_boundaries_fixed(self, text: str, threshold: float) -> List[int]:
227
+ """Get all semantic boundaries without sequence length warnings"""
228
+
229
+ # Check text length first and use chunked approach if needed
230
+ rough_token_count = len(text.split()) * ROUGH_TOKEN_MULTIPLIER
231
+
232
+ if rough_token_count > LONG_TEXT_THRESHOLD:
233
+ return self._get_boundaries_for_long_text(text, threshold)
234
+
235
+ # For shorter texts, use standard approach with truncation
236
+ full_encoding = self.tokenizer(
237
+ text,
238
+ return_tensors="pt",
239
+ return_offsets_mapping=True,
240
+ add_special_tokens=True,
241
+ truncation=True, # Enable truncation to avoid warnings
242
+ max_length=BERT_MAX_LENGTH, # Standard BERT limit
243
+ padding=False
244
+ )
245
+
246
+ input_ids = full_encoding['input_ids'][0]
247
+ offset_mapping = full_encoding['offset_mapping'][0]
248
+
249
+ # Remove special tokens (CLS at start, SEP at end)
250
+ content_input_ids = input_ids[1:-1]
251
+ content_offsets = offset_mapping[1:-1]
252
+
253
+ if len(content_input_ids) == 0:
254
+ return []
255
+
256
+ # Process with single window since we truncated
257
+ boundaries = self._process_single_window_safe(
258
+ text, input_ids, content_offsets, threshold
259
+ )
260
+
261
+ # Remove duplicates and sort
262
+ boundaries = sorted(list(set(boundaries)))
263
+
264
+ # Remove boundaries at text start/end
265
+ boundaries = [b for b in boundaries if 0 < b < len(text)]
266
+
267
+ return boundaries
268
+
269
+ def _get_boundaries_for_long_text(self, text: str, threshold: float) -> List[int]:
270
+ """Handle long texts by processing in semantic chunks"""
271
+
272
+ # Split text into rough sections first (by sentences)
273
+ import re
274
+ sentences = re.split(r'[.!?]+', text)
275
+
276
+ all_boundaries = []
277
+ current_pos = 0
278
+
279
+ # Process each section that fits in model limits
280
+ current_section = ""
281
+
282
+ for sentence in sentences:
283
+ test_section = current_section + sentence + "."
284
+ rough_tokens = len(test_section.split()) * ROUGH_TOKEN_MULTIPLIER
285
+
286
+ if rough_tokens > LONG_TEXT_THRESHOLD and current_section:
287
+ # Process current section
288
+ section_boundaries = self._process_text_section(
289
+ current_section, current_pos, threshold
290
+ )
291
+ all_boundaries.extend(section_boundaries)
292
+
293
+ # Start new section
294
+ current_pos += len(current_section)
295
+ current_section = sentence + "."
296
+ else:
297
+ current_section = test_section
298
+
299
+ # Process remaining section
300
+ if current_section:
301
+ section_boundaries = self._process_text_section(
302
+ current_section, current_pos, threshold
303
+ )
304
+ all_boundaries.extend(section_boundaries)
305
+
306
+ return sorted(list(set(all_boundaries)))
307
+
308
+ def _process_text_section(self, section: str, offset: int, threshold: float) -> List[int]:
309
+ """Process a section of text that fits in model limits"""
310
+
311
+ try:
312
+ # Tokenize section (should be safe now)
313
+ encoding = self.tokenizer(
314
+ section,
315
+ return_tensors="pt",
316
+ return_offsets_mapping=True,
317
+ add_special_tokens=True,
318
+ truncation=True,
319
+ max_length=BERT_MAX_LENGTH
320
+ )
321
+
322
+ input_ids = encoding['input_ids'][0]
323
+ offset_mapping = encoding['offset_mapping'][0]
324
+
325
+ # Remove special tokens
326
+ content_offsets = offset_mapping[1:-1]
327
+
328
+ # Get model predictions
329
+ with torch.no_grad():
330
+ outputs = self.model(encoding['input_ids'].to(self.device))
331
+ probabilities = F.softmax(outputs.logits, dim=-1)
332
+
333
+ # Extract B-CHUNK probabilities
334
+ b_chunk_id = self.model.config.label2id.get("B-CHUNK", 1)
335
+ chunk_probs = probabilities[0, 1:-1, b_chunk_id].cpu().numpy()
336
+
337
+ # Find boundaries
338
+ boundaries = []
339
+ for i, (prob, char_offset) in enumerate(zip(chunk_probs, content_offsets)):
340
+ if prob > threshold:
341
+ char_start = char_offset[0].item()
342
+ char_end = char_offset[1].item()
343
+
344
+ # Find clean boundary in full text
345
+ boundary_pos = self._find_clean_boundary_global(
346
+ section, char_start, char_end, offset
347
+ )
348
+ if boundary_pos is not None and boundary_pos > offset:
349
+ boundaries.append(boundary_pos)
350
+
351
+ return boundaries
352
+
353
+ except Exception as e:
354
+ print(f"Warning: Error processing section: {e}")
355
+ return []
356
+
357
+ def _find_clean_boundary_global(self, section: str, char_start: int,
358
+ char_end: int, global_offset: int) -> Optional[int]:
359
+ """Find clean boundary and return global position"""
360
+
361
+ local_boundary = self._find_clean_boundary(section, char_start, char_end)
362
+ if local_boundary is not None:
363
+ global_pos = global_offset + local_boundary
364
+
365
+ # Additional validation: ensure we don't cut words in half
366
+ if global_pos > 0 and global_pos < len(section) + global_offset:
367
+ return global_pos
368
+ return None
369
+
370
+ def _process_single_window_safe(self, text: str, input_ids: torch.Tensor,
371
+ content_offsets: torch.Tensor, threshold: float) -> List[int]:
372
+ """Process text that fits in a single window safely"""
373
+
374
+ try:
375
+ # Get model prediction
376
+ with torch.no_grad():
377
+ input_ids_batch = input_ids.unsqueeze(0).to(self.device)
378
+ outputs = self.model(input_ids_batch)
379
+ probabilities = F.softmax(outputs.logits, dim=-1)
380
+
381
+ # Extract B-CHUNK probabilities for content tokens only
382
+ b_chunk_id = self.model.config.label2id.get("B-CHUNK", 1)
383
+ chunk_probs = probabilities[0, 1:-1, b_chunk_id].cpu().numpy()
384
+
385
+ # Find boundaries
386
+ boundaries = []
387
+ for i, (prob, offset) in enumerate(zip(chunk_probs, content_offsets)):
388
+ if prob > threshold:
389
+ char_start = offset[0].item()
390
+ char_end = offset[1].item()
391
+
392
+ # Find clean boundary position
393
+ boundary_pos = self._find_clean_boundary(text, char_start, char_end)
394
+ if boundary_pos is not None and boundary_pos > 0:
395
+ boundaries.append(boundary_pos)
396
+
397
+ return boundaries
398
+
399
+ except Exception as e:
400
+ print(f"Warning: Error in single window processing: {e}")
401
+ return []
402
+
403
+ def _find_clean_boundary(self, text: str, char_start: int, char_end: int) -> Optional[int]:
404
+ """Find a clean boundary near the predicted position, prioritizing sentence starts"""
405
+
406
+ # Ensure positions are within text bounds
407
+ char_start = max(0, min(char_start, len(text) - 1))
408
+ char_end = max(0, min(char_end, len(text)))
409
+
410
+ # Search range around the token
411
+ search_start = max(0, char_start - SEARCH_RANGE)
412
+ search_end = min(len(text), char_end + SEARCH_RANGE)
413
+
414
+ # Priority 1: Look for sentence endings followed by capital letters (forward search)
415
+ for i in range(char_start, search_end):
416
+ if i < len(text) and text[i] in '.!?':
417
+ # Look for the start of next sentence
418
+ boundary = i + 1
419
+ # Skip whitespace
420
+ while boundary < len(text) and text[boundary] in ' \t\n':
421
+ boundary += 1
422
+ # Check if next character is uppercase (start of sentence)
423
+ if boundary < len(text) and (text[boundary].isupper() or text[boundary].isdigit()):
424
+ return boundary
425
+
426
+ # Priority 2: Search backwards for sentence endings followed by capitals
427
+ for i in range(char_start - 1, search_start - 1, -1):
428
+ if i >= 0 and text[i] in '.!?':
429
+ boundary = i + 1
430
+ # Skip whitespace
431
+ while boundary < len(text) and text[boundary] in ' \t\n':
432
+ boundary += 1
433
+ # Check if next character is uppercase (start of sentence)
434
+ if boundary < len(text) and (text[boundary].isupper() or text[boundary].isdigit()):
435
+ return boundary
436
+
437
+ # Priority 3: Word boundaries (spaces) only if followed by capital
438
+ for i in range(char_start, search_end):
439
+ if i < len(text) and text[i] in ' \t':
440
+ boundary = i + 1
441
+ while boundary < len(text) and text[boundary] in ' \t':
442
+ boundary += 1
443
+ # Only use if followed by capital letter
444
+ if boundary < len(text) and text[boundary].isupper():
445
+ return boundary
446
+
447
+ # Fallback: use the end of the token
448
+ fallback_pos = char_end if char_end <= len(text) else len(text)
449
+ return fallback_pos
450
+
451
+ def _split_at_boundaries(self, text: str, boundaries: List[int]) -> List[str]:
452
+ """Split text at boundaries ensuring no gaps or overlaps and proper sentence starts"""
453
+ if not boundaries:
454
+ return [text]
455
+
456
+ chunks = []
457
+ start = 0
458
+
459
+ for boundary in boundaries:
460
+ # Ensure boundary is within text
461
+ boundary = min(boundary, len(text))
462
+
463
+ if start < boundary:
464
+ chunk = text[start:boundary].strip()
465
+ if chunk: # Only add non-empty chunks
466
+ chunks.append(chunk)
467
+ start = boundary
468
+
469
+ # Add remaining text
470
+ if start < len(text):
471
+ remaining = text[start:].strip()
472
+ if remaining:
473
+ chunks.append(remaining)
474
+
475
+ return chunks
476
+
477
+ def _optimize_chunk_sizes(self, chunks: List[str], max_tokens: int, target_tokens: int) -> List[str]:
478
+ """Fully dynamic chunk size optimization"""
479
+ if not chunks:
480
+ return []
481
+
482
+ # Calculate dynamic thresholds based on target
483
+ good_size_min = target_tokens * GOOD_SIZE_MIN_RATIO
484
+ good_size_max = max_tokens # Use actual max
485
+ merge_threshold = target_tokens * MERGE_THRESHOLD_RATIO
486
+
487
+ optimized = []
488
+ i = 0
489
+
490
+ while i < len(chunks):
491
+ current_chunk = chunks[i]
492
+ current_tokens = self._count_tokens(current_chunk)
493
+
494
+ # If chunk is in good size range, keep it
495
+ if good_size_min <= current_tokens <= good_size_max:
496
+ optimized.append(current_chunk)
497
+ i += 1
498
+ continue
499
+
500
+ # If chunk is too large, split it
501
+ if current_tokens > good_size_max:
502
+ split_chunks = self._split_large_chunk_dynamic(
503
+ current_chunk, max_tokens, target_tokens
504
+ )
505
+ optimized.extend(split_chunks)
506
+ i += 1
507
+ continue
508
+
509
+ # If chunk is too small, try to merge with next chunks
510
+ if current_tokens < merge_threshold:
511
+ merged_chunk, chunks_consumed = self._merge_small_chunks_dynamic(
512
+ chunks[i:], max_tokens, target_tokens
513
+ )
514
+ optimized.append(merged_chunk)
515
+ i += chunks_consumed
516
+ continue
517
+
518
+ # Default: keep the chunk (it's in acceptable range)
519
+ optimized.append(current_chunk)
520
+ i += 1
521
+
522
+ return optimized
523
+
524
+ def _count_tokens(self, text: str) -> int:
525
+ """Count tokens in text"""
526
+ if not text.strip():
527
+ return 0
528
+ encoding = self.tokenizer(text, add_special_tokens=False)
529
+ return len(encoding['input_ids'])
530
+
531
+ def _merge_small_chunks_dynamic(self, chunks: List[str], max_tokens: int, target_tokens: int) -> Tuple[str, int]:
532
+ """Dynamic merging based on target size"""
533
+ if not chunks:
534
+ return "", 0
535
+
536
+ merged = chunks[0]
537
+ consumed = 1
538
+ merged_tokens = self._count_tokens(merged)
539
+
540
+ # Dynamic target: aim for target_tokens but stop before max_tokens
541
+ optimal_target = min(target_tokens, max_tokens * OPTIMAL_TARGET_RATIO)
542
+
543
+ # Try to merge with following chunks
544
+ for i in range(1, len(chunks)):
545
+ candidate = merged + " " + chunks[i]
546
+ candidate_tokens = self._count_tokens(candidate)
547
+
548
+ # Stop if we exceed max tokens
549
+ if candidate_tokens > max_tokens:
550
+ break
551
+
552
+ # Merge and continue
553
+ merged = candidate
554
+ merged_tokens = candidate_tokens
555
+ consumed += 1
556
+
557
+ # Stop if we reached optimal target
558
+ if merged_tokens >= optimal_target:
559
+ break
560
+
561
+ return merged, consumed
562
+
563
+ def _split_large_chunk_dynamic(self, chunk: str, max_tokens: int, target_tokens: int) -> List[str]:
564
+ """Dynamic splitting based on target size"""
565
+ result = []
566
+ remaining = chunk
567
+
568
+ while remaining:
569
+ current_tokens = self._count_tokens(remaining)
570
+
571
+ # If remaining fits in max size, add it
572
+ if current_tokens <= max_tokens:
573
+ result.append(remaining)
574
+ break
575
+
576
+ # Find optimal split point (prefer target_tokens, but respect max_tokens)
577
+ optimal_split = min(target_tokens, max_tokens * OPTIMAL_TARGET_RATIO)
578
+ split_pos = self._find_optimal_split_position(remaining, optimal_split, max_tokens)
579
+
580
+ if split_pos > 0 and split_pos < len(remaining):
581
+ chunk_part = remaining[:split_pos].strip()
582
+ if chunk_part:
583
+ result.append(chunk_part)
584
+ remaining = remaining[split_pos:].strip()
585
+ else:
586
+ # Fallback: force split at max_tokens
587
+ split_pos = self._find_token_split_position(remaining, max_tokens)
588
+ if split_pos > 0:
589
+ chunk_part = remaining[:split_pos].strip()
590
+ if chunk_part:
591
+ result.append(chunk_part)
592
+ remaining = remaining[split_pos:].strip()
593
+ else:
594
+ # Last resort: take the remaining text
595
+ result.append(remaining)
596
+ break
597
+
598
+ return [r for r in result if r.strip()]
599
+
600
+ def _find_optimal_split_position(self, text: str, target_tokens: int, max_tokens: int) -> int:
601
+ """Find optimal split position aiming for target_tokens but not exceeding max_tokens"""
602
+ # Binary search for position closest to target_tokens
603
+ left, right = 0, len(text)
604
+ best_pos = 0
605
+ best_tokens = 0
606
+
607
+ while left <= right:
608
+ mid = (left + right) // 2
609
+ test_text = text[:mid]
610
+
611
+ if not test_text.strip():
612
+ left = mid + 1
613
+ continue
614
+
615
+ tokens = self._count_tokens(test_text)
616
+
617
+ if tokens <= max_tokens:
618
+ # This position is valid, check if it's closer to target
619
+ if abs(tokens - target_tokens) < abs(best_tokens - target_tokens) or best_tokens == 0:
620
+ best_pos = mid
621
+ best_tokens = tokens
622
+
623
+ if tokens < target_tokens:
624
+ left = mid + 1 # Try to get closer to target
625
+ else:
626
+ break # We've reached or exceeded target
627
+ else:
628
+ right = mid - 1
629
+
630
+ # Refine to find clean boundary
631
+ if best_pos > 0:
632
+ clean_pos = self._find_char_split_position(text, best_pos)
633
+ return clean_pos
634
+
635
+ return best_pos
636
+
637
+ def _find_token_split_position(self, text: str, max_tokens: int) -> int:
638
+ """Find a good position to split text within token limit"""
639
+ # Binary search approach for accurate token-based splitting
640
+ left, right = 0, len(text)
641
+ best_pos = 0
642
+
643
+ while left <= right:
644
+ mid = (left + right) // 2
645
+
646
+ # Test if text[:mid] fits within token limit
647
+ test_text = text[:mid]
648
+ if not test_text.strip():
649
+ left = mid + 1
650
+ continue
651
+
652
+ encoding = self.tokenizer(test_text, add_special_tokens=False)
653
+ token_count = len(encoding['input_ids'])
654
+
655
+ if token_count <= max_tokens:
656
+ best_pos = mid
657
+ left = mid + 1
658
+ else:
659
+ right = mid - 1
660
+
661
+ return best_pos
662
+
663
+ def _find_char_split_position(self, text: str, max_chars: int) -> int:
664
+ """Find a good position to split text within character limit"""
665
+ if max_chars >= len(text):
666
+ return len(text)
667
+
668
+ # Look for sentence endings before the limit
669
+ search_start = max(0, max_chars - SENTENCE_SEARCH_RANGE)
670
+ for i in range(min(max_chars, len(text)) - 1, search_start, -1):
671
+ if i < len(text) and text[i] in '.!?':
672
+ # Skip forward past any whitespace
673
+ boundary = i + 1
674
+ while boundary < len(text) and text[boundary] in ' \t\n':
675
+ boundary += 1
676
+ return min(boundary, len(text))
677
+
678
+ # Look for spaces before the limit
679
+ search_start = max(0, max_chars - SPACE_SEARCH_RANGE)
680
+ for i in range(min(max_chars, len(text)) - 1, search_start, -1):
681
+ if i < len(text) and text[i] in ' \t':
682
+ return i + 1
683
+
684
+ # Fallback: split at the limit
685
+ return min(max_chars, len(text))
686
+
687
+ def _apply_char_limits(self, chunks: List[str], max_chars: int) -> List[str]:
688
+ """Apply character limits to optimized chunks"""
689
+ result = []
690
+
691
+ def _apply_char_limits(self, chunks: List[str], max_chars: int) -> List[str]:
692
+ """Apply character limits to optimized chunks"""
693
+ result = []
694
+
695
+ for chunk in chunks:
696
+ if len(chunk) <= max_chars:
697
+ result.append(chunk)
698
+ else:
699
+ # Split by character limit
700
+ sub_chunks = self._split_by_chars(chunk, max_chars)
701
+ result.extend(sub_chunks)
702
+
703
+ return result
704
+
705
+ def _split_by_chars(self, chunk: str, max_chars: int) -> List[str]:
706
+ """Split chunk by character limit only"""
707
+ result = []
708
+ remaining = chunk
709
+
710
+ while remaining:
711
+ if len(remaining) <= max_chars:
712
+ result.append(remaining)
713
+ break
714
+
715
+ # Find a good split point within char limit
716
+ split_pos = self._find_char_split_position(remaining, max_chars)
717
+
718
+ if split_pos > 0 and split_pos < len(remaining):
719
+ result.append(remaining[:split_pos].strip())
720
+ remaining = remaining[split_pos:].strip()
721
+ else:
722
+ # Fallback: force split at limit
723
+ result.append(remaining[:max_chars].strip())
724
+ remaining = remaining[max_chars:].strip()
725
+
726
+ return [r for r in result if r.strip()]
727
+
728
+ def _clean_and_validate_chunks(self, chunks: List[str], target_tokens: int, max_tokens: int) -> List[str]:
729
+ """Dynamic cleaning and validation - fully adaptive to target size"""
730
+
731
+ if not chunks:
732
+ return []
733
+
734
+ # Dynamic thresholds based on target
735
+ dynamic_min_length = max(10, target_tokens // MIN_LENGTH_DIVISOR)
736
+ merge_threshold = target_tokens * MERGE_ATTEMPT_RATIO
737
+ optimal_max = max_tokens * OPTIMAL_MAX_RATIO
738
+
739
+ # Remove very small chunks first
740
+ cleaned = [chunk.strip() for chunk in chunks if len(chunk.strip()) >= dynamic_min_length]
741
+
742
+ if not cleaned:
743
+ return chunks # Return original if all chunks are removed
744
+
745
+ # Dynamic merging for better chunk sizes
746
+ final = []
747
+ i = 0
748
+
749
+ while i < len(cleaned):
750
+ current = cleaned[i]
751
+ current_tokens = self._count_tokens(current)
752
+
753
+ # If current chunk is small, try to merge
754
+ if current_tokens < merge_threshold:
755
+ merged_successfully = False
756
+
757
+ # Try to merge with previous chunk if it exists and is not too large
758
+ if final:
759
+ prev_tokens = self._count_tokens(final[-1])
760
+ combined = final[-1] + " " + current
761
+ combined_tokens = self._count_tokens(combined)
762
+
763
+ if combined_tokens <= optimal_max and combined_tokens <= target_tokens * MAX_MERGE_RATIO:
764
+ final[-1] = combined
765
+ merged_successfully = True
766
+
767
+ # If couldn't merge with previous, try to merge with next chunks
768
+ if not merged_successfully and i + 1 < len(cleaned):
769
+ merged_chunk, chunks_consumed = self._merge_small_chunks_dynamic(
770
+ cleaned[i:], max_tokens, target_tokens
771
+ )
772
+ final.append(merged_chunk)
773
+ i += chunks_consumed
774
+ continue
775
+
776
+ # If still couldn't merge, add as is (unless it's too small)
777
+ if not merged_successfully:
778
+ min_acceptable = target_tokens * MIN_ACCEPTABLE_RATIO
779
+ if current_tokens >= min_acceptable:
780
+ final.append(current)
781
+ elif final: # Try one more time to merge with previous
782
+ prev_tokens = self._count_tokens(final[-1])
783
+ combined = final[-1] + " " + current
784
+ combined_tokens = self._count_tokens(combined)
785
+ if combined_tokens <= optimal_max:
786
+ final[-1] = combined
787
+ else:
788
+ final.append(current) # Keep as separate chunk
789
+ else:
790
+ final.append(current) # First chunk, keep it
791
+ else:
792
+ final.append(current)
793
+
794
+ i += 1
795
+
796
+ return final
797
+
798
+
799
+ def chunk_azerbaijani_text(text: str,
800
+ model_path: str = MODEL_PATH,
801
+ max_chunk_tokens: Optional[int] = MAX_CHUNK_TOKENS,
802
+ target_tokens: Optional[int] = TARGET_TOKENS,
803
+ max_chunk_chars: Optional[int] = MAX_CHUNK_CHARS,
804
+ threshold: float = THRESHOLD,
805
+ priority: str = PRIORITY) -> List[str]:
806
+ """
807
+ Simplified function to chunk Azerbaijani text with single parameter configuration
808
+
809
+ Args:
810
+ text: Input text to chunk
811
+ model_path: HuggingFace model path
812
+ max_chunk_tokens: Maximum tokens per chunk
813
+ target_tokens: Target optimal size in tokens
814
+ max_chunk_chars: Maximum characters per chunk
815
+ threshold: Confidence threshold for splitting
816
+ priority: Which limit to prioritize when both are set
817
+
818
+ Returns:
819
+ List of optimally-sized text chunks
820
+ """
821
+ chunker = AzerbaijaniTextChunker(model_path)
822
+ return chunker.chunk(text, max_chunk_tokens, target_tokens, max_chunk_chars, threshold, priority)
823
+
824
+
825
+ def main():
826
+ """Main function demonstrating the simplified chunker"""
827
+
828
+ print("=== SIMPLIFIED AZERBAIJANI TEXT CHUNKER ===\n")
829
+
830
+ # Show current configuration
831
+ print("CURRENT CONFIGURATION:")
832
+ print(f"Model path: {MODEL_PATH}")
833
+ print(f"Max tokens: {MAX_CHUNK_TOKENS}")
834
+ print(f"Target tokens: {TARGET_TOKENS}")
835
+ print(f"Max chars: {MAX_CHUNK_CHARS}")
836
+ print(f"Threshold: {THRESHOLD}")
837
+ print(f"Priority: {PRIORITY}")
838
+ print("="*60 + "\n")
839
+
840
+ # Basic chunking with global settings
841
+ print(f"Chunking with global settings (target ~{TARGET_TOKENS} tokens, max {MAX_CHUNK_TOKENS}):")
842
+ chunks = chunk_azerbaijani_text(SAMPLE_TEXT)
843
+
844
+ total_tokens = 0
845
+ text_preview_main = 80 # Preview length for main chunks
846
+
847
+ for i, chunk in enumerate(chunks, 1):
848
+ try:
849
+ from transformers import AutoTokenizer
850
+ tokenizer = AutoTokenizer.from_pretrained('xlm-roberta-base')
851
+ tokens = tokenizer(chunk, add_special_tokens=False)['input_ids']
852
+ token_count = len(tokens)
853
+ total_tokens += token_count
854
+ except:
855
+ token_count = "N/A"
856
+
857
+ print(f"{i}. [{len(chunk)} chars, {token_count} tokens]")
858
+ print(f" {chunk[:text_preview_main]}{'...' if len(chunk) > text_preview_main else ''}\n")
859
+
860
+ print(f"Total chunks: {len(chunks)}")
861
+ if total_tokens > 0:
862
+ print(f"Total tokens: {total_tokens}")
863
+ print(f"Average tokens per chunk: {total_tokens/len(chunks):.1f}")
864
+
865
+ print("="*60 + "\n")
866
+
867
+
868
+
869
+ # Example usage
870
+ if __name__ == "__main__":
871
+ main()
872
+ ```
873
+
874
+ ```bash
875
+ # Results
876
+
877
+ Chunking with global settings (target ~256 tokens, max 400):
878
+ 1. [1247 chars, 288 tokens]
879
+ Azərbaycan Respublikası, Qafqaz regionunda yerləşən, unikal coğrafi mövqeyi, zən...
880
+
881
+ 2. [1007 chars, 247 tokens]
882
+ Azərbaycanın təbiəti də onun mədəni zənginliyi qədər heyranedici və müxtəlifdir....
883
+
884
+ 3. [1027 chars, 197 tokens]
885
+ Rəqəmsal transformasiya, süni intellekt (AI), maşın təlimi (ML), böyük verilənlə...
886
+
887
+ 4. [1128 chars, 222 tokens]
888
+ Azərbaycan tarixi boyu bir çox mühüm hadisələrə şahidlik etmişdir. Qədim dövrlər...
889
+
890
+ 5. [1194 chars, 256 tokens]
891
+ Azərbaycanın gələcəkə baxışı, həm ölkə daxilində, həm də beynəlxalq aləmdə özünü...
892
+
893
+ 6. [1412 chars, 277 tokens]
894
+ Beynəlxalq əməkdaşlığın genişləndirilməsi, Avropa İttifaqı və digər beynəlxalq t...
895
+
896
+ 7. [1224 chars, 229 tokens]
897
+ Elmi tədqiqatlar sahəsində də böyük irəliləyişlər müşahidə olunur. Milli Elmlər ...
898
+
899
+ 8. [857 chars, 179 tokens]
900
+ Tarixi Azərbaycan torpaqları, həm də zəngin folklora, ədəbiyyata və incəsənətə m...
901
+
902
+ Total chunks: 8
903
+ Total tokens: 1895
904
+ Average tokens per chunk: 236.9
905
+ ```
906
+
907
+
908
+ # Quick Configuration for Chunking
909
+
910
+ ## Global Parameters
911
+
912
+ | Parameter | Value | Description |
913
+ |--------------------|-------|---------------------------------------------------------------|
914
+ | MAX_CHUNK_TOKENS | 300 | Maximum tokens per chunk (must be < model context window) |
915
+ | TARGET_TOKENS | 200 | Target optimal chunk size |
916
+ | THRESHOLD | 0.12 | Semantic boundary confidence (0.10–0.20) |
917
 
918
+ ---
919
 
920
+ ## Configuration for 384-token Embedding Models
921
 
922
+ ### Recommended Settings
923
 
924
+ | Parameter | Value | Notes |
925
+ |--------------------|-------|--------------------------------------------------|
926
+ | MAX_CHUNK_TOKENS | 300 | 78% of context window (384 × 0.78) |
927
+ | TARGET_TOKENS | 200 | Two-thirds of max tokens |
928
+ | THRESHOLD | 0.12 | Balanced segmentation |
929
 
930
+ ### Conservative Settings
931
 
932
+ | Parameter | Value | Notes |
933
+ |--------------------|-------|--------------------------------------------------|
934
+ | MAX_CHUNK_TOKENS | 256 | ~67% of context window |
935
+ | TARGET_TOKENS | 180 | ~70% of max tokens |
936
+ | THRESHOLD | 0.15 | Fewer, larger chunks |
937
 
938
+ ### Aggressive Settings
939
 
940
+ | Parameter | Value | Notes |
941
+ |--------------------|-------|--------------------------------------------------|
942
+ | MAX_CHUNK_TOKENS | 320 | 83% of context window |
943
+ | TARGET_TOKENS | 220 | Close to max tokens |
944
+ | THRESHOLD | 0.10 | More boundaries, finer segmentation |
945
 
946
+ ---
 
 
 
 
947
 
948
+ ## Key Rules
949
 
950
+ - MAX_CHUNK_TOKENS should be 20–25% less than the embedding model’s context window
951
+ - TARGET_TOKENS should be 60–80% of MAX_CHUNK_TOKENS
952
+ - THRESHOLD controls granularity:
953
+ - Lower → more chunks
954
+ - Higher → fewer chunks
955
+ - Always test with your specific embedding model and adjust as needed
956
 
957
+ ---
958
 
959
+ ## Expected Results
960
 
961
+ - Average chunk size: ~TARGET_TOKENS ± 20%
962
+ - All chunks: < MAX_CHUNK_TOKENS
963
+ - Semantic boundaries preserved
964
+ - No text loss or duplication
965
 
 
966
 
 
967
 
968
+ # Use Cases
969
 
970
+ ## Perfect for RAG Systems
971
+ - **Vector Databases**: Ensure chunks fit embedding model limits
972
+ - **Search Applications**: Optimal chunk sizes for retrieval
973
+ - **Question Answering**: Maintain semantic coherence
974
 
975
+ ## Document Processing
976
+ - **Academic Papers**: Respect section and paragraph boundaries
977
+ - **Legal Documents**: Maintain clause integrity
978
+ - **News Articles**: Preserve story flow and context
979
 
980
+ ## Content Management
981
+ - **CMS Integration**: Automatic content segmentation
982
+ - **API Limits**: Respect external service constraints
983
+ - **Storage Optimization**: Consistent chunk sizes for databases
984
 
985
+ ---
986
 
987
+ # Chunking Strategies
988
 
989
+ ## Optimal Strategy (Default)
990
+ - **Threshold**: 0.13
991
+ - **Best for**: General purpose, balanced precision/recall
992
+ - **Typical output**: 3–5 chunks for medium texts
993
 
994
+ ## Conservative Strategy
995
+ - **Threshold**: 0.3
996
+ - **Best for**: Longer chunks, fewer segments
997
+ - **Typical output**: 1–3 chunks for medium texts
998
 
999
+ ## Aggressive Strategy
1000
+ - **Threshold**: 0.05
1001
+ - **Best for**: Fine-grained segmentation
1002
+ - **Typical output**: 5–10 chunks for medium texts
1003
 
1004
+ ---
1005
 
1006
+ ## CC BY 4.0 License — What It Allows
1007
 
1008
+ The **Creative Commons Attribution 4.0 International (CC BY 4.0)** license allows:
1009
 
1010
+ You are free to use, modify, and distribute the model — even for commercial purposes — as long as you give proper credit to the original creator.
1011
 
1012
+ For more information, please refer to the <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/deed.en">CC BY 4.0 license</a>.
1013
 
 
1014
 
1015
+ ## Contact
1016
 
1017
+ For more information, questions, or issues, please contact LocalDoc at [[email protected]].