fine 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -10
- data/docs/examples/image-classification-shapes.md +83 -0
- data/docs/examples/text-embeddings-faq.md +98 -0
- data/docs/quickstart.md +209 -0
- data/docs/tutorials/lora-tool-calling.md +306 -0
- data/examples/data/generate_tool_data.rb +261 -0
- data/examples/data/ollama_tool_calls.jsonl +40 -0
- data/examples/data/sentiment_reviews.jsonl +30 -0
- data/examples/data/shapes/circle/circle_1.jpg +0 -0
- data/examples/data/shapes/circle/circle_10.jpg +0 -0
- data/examples/data/shapes/circle/circle_2.jpg +0 -0
- data/examples/data/shapes/circle/circle_3.jpg +0 -0
- data/examples/data/shapes/circle/circle_4.jpg +0 -0
- data/examples/data/shapes/circle/circle_5.jpg +0 -0
- data/examples/data/shapes/circle/circle_6.jpg +0 -0
- data/examples/data/shapes/circle/circle_7.jpg +0 -0
- data/examples/data/shapes/circle/circle_8.jpg +0 -0
- data/examples/data/shapes/circle/circle_9.jpg +0 -0
- data/examples/data/shapes/square/square_1.jpg +0 -0
- data/examples/data/shapes/square/square_10.jpg +0 -0
- data/examples/data/shapes/square/square_2.jpg +0 -0
- data/examples/data/shapes/square/square_3.jpg +0 -0
- data/examples/data/shapes/square/square_4.jpg +0 -0
- data/examples/data/shapes/square/square_5.jpg +0 -0
- data/examples/data/shapes/square/square_6.jpg +0 -0
- data/examples/data/shapes/square/square_7.jpg +0 -0
- data/examples/data/shapes/square/square_8.jpg +0 -0
- data/examples/data/shapes/square/square_9.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_1.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_10.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_2.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_3.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_4.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_5.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_6.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_7.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_8.jpg +0 -0
- data/examples/data/shapes/triangle/triangle_9.jpg +0 -0
- data/examples/data/support_faq_pairs.jsonl +30 -0
- data/examples/generate_shape_images.rb +94 -0
- data/examples/sentiment_classification.rb +87 -0
- data/examples/shape_classification.rb +87 -0
- data/examples/support_faq_embeddings.rb +105 -0
- data/examples/train_lora_tools.rb +218 -0
- data/lib/fine/configuration.rb +173 -15
- data/lib/fine/datasets/image_dataset.rb +14 -2
- data/lib/fine/datasets/instruction_dataset.rb +17 -2
- data/lib/fine/datasets/text_dataset.rb +15 -5
- data/lib/fine/hub/config_loader.rb +4 -4
- data/lib/fine/hub/safetensors_loader.rb +3 -2
- data/lib/fine/llm.rb +39 -10
- data/lib/fine/lora.rb +214 -0
- data/lib/fine/models/bert_encoder.rb +15 -6
- data/lib/fine/models/bert_for_sequence_classification.rb +35 -4
- data/lib/fine/models/causal_lm.rb +46 -5
- data/lib/fine/models/gemma3_decoder.rb +25 -6
- data/lib/fine/models/llama_decoder.rb +9 -8
- data/lib/fine/models/sentence_transformer.rb +1 -1
- data/lib/fine/tokenizers/auto_tokenizer.rb +15 -0
- data/lib/fine/training/text_trainer.rb +3 -1
- data/lib/fine/validators.rb +304 -0
- data/lib/fine/version.rb +1 -1
- data/lib/fine.rb +4 -0
- metadata +47 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e4f1a43c08a5a1f84d56676c0f8831cab888481c7011bee1d81ef09cbf3ce239
|
|
4
|
+
data.tar.gz: 0330fc12a8a1cbd25a58e86a733250b14d1f5372d194f4b86f80f1c8c073a266
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4fa0df707889ed969d75b1315ef3b98013015acbb4954cfaf78027506c104c17ed1f558871a3524e2327b1031f2d3768bc94797875934fd24418d80046fd079
|
|
7
|
+
data.tar.gz: 0b5f0ad3c7952b39f15f6fa3f8fed858d9b31b26e1fbeceb2afcf9697803b10c236e5257fdef855b30c15b90dc053c0aacbb36a62adbf8762c771d11f074cc74
|
data/README.md
CHANGED
|
@@ -90,19 +90,27 @@ similarity = cosine_similarity(embedding1, embedding2)
|
|
|
90
90
|
|
|
91
91
|
---
|
|
92
92
|
|
|
93
|
-
### LLMs
|
|
93
|
+
### LLMs (with LoRA)
|
|
94
94
|
|
|
95
|
-
Fine-tune Gemma, Llama, Qwen and other open models
|
|
95
|
+
Fine-tune Gemma, Llama, Qwen and other open models using LoRA—train only 0.5% of parameters.
|
|
96
96
|
|
|
97
97
|
```ruby
|
|
98
|
-
|
|
99
|
-
llm.fit(train_file: "instructions.jsonl", epochs: 3)
|
|
98
|
+
model = Fine::Models::CausalLM.from_pretrained("google/gemma-3-1b-it")
|
|
100
99
|
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
# Apply LoRA - only 0.5% of params trainable
|
|
101
|
+
Fine::LoRA.apply(model, rank: 32, alpha: 64)
|
|
102
|
+
# Trainable params: 5.96M (0.46%)
|
|
103
|
+
|
|
104
|
+
# Train on your data
|
|
105
|
+
trainer = Fine::Training::LLMTrainer.new(model, config, train_dataset: dataset)
|
|
106
|
+
trainer.fit
|
|
107
|
+
|
|
108
|
+
# Merge weights and save
|
|
109
|
+
Fine::LoRA.merge!(model)
|
|
110
|
+
model.save("my_model")
|
|
103
111
|
```
|
|
104
112
|
|
|
105
|
-
[Full tutorial: LLM Fine-tuning](docs/tutorials/llm-fine-tuning.md)
|
|
113
|
+
[Full tutorial: LLM Fine-tuning](docs/tutorials/llm-fine-tuning.md) | [LoRA Tool Calling](docs/tutorials/lora-tool-calling.md)
|
|
106
114
|
|
|
107
115
|
---
|
|
108
116
|
|
|
@@ -114,7 +122,7 @@ gem 'fine'
|
|
|
114
122
|
|
|
115
123
|
Requires Ruby 3.1+, LibTorch, and libvips.
|
|
116
124
|
|
|
117
|
-
[Full installation guide](docs/installation.md)
|
|
125
|
+
[Full installation guide](docs/installation.md) | [Quickstart](docs/quickstart.md)
|
|
118
126
|
|
|
119
127
|
**Quick setup (macOS):**
|
|
120
128
|
```bash
|
|
@@ -155,8 +163,9 @@ bundle install
|
|
|
155
163
|
|
|
156
164
|
| Model | Parameters | Best For |
|
|
157
165
|
|-------|------------|----------|
|
|
166
|
+
| `google/gemma-3-1b-it` | 1B | Fast experiments, tool calling |
|
|
158
167
|
| `meta-llama/Llama-3.2-1B` | 1B | Fast experiments |
|
|
159
|
-
| `google/gemma-
|
|
168
|
+
| `google/gemma-3-4b-it` | 4B | Good balance |
|
|
160
169
|
| `Qwen/Qwen2-1.5B` | 1.5B | Multilingual |
|
|
161
170
|
| `mistralai/Mistral-7B-v0.1` | 7B | Best quality |
|
|
162
171
|
|
|
@@ -201,7 +210,8 @@ llm.export_gguf("model.gguf", quantization: :q4_0)
|
|
|
201
210
|
- [x] Text embedding models
|
|
202
211
|
- [x] LLM fine-tuning (Gemma, Llama, Qwen)
|
|
203
212
|
- [x] ONNX & GGUF export
|
|
204
|
-
- [
|
|
213
|
+
- [x] LoRA fine-tuning
|
|
214
|
+
- [ ] QLoRA (4-bit quantized LoRA)
|
|
205
215
|
|
|
206
216
|
## Contributing
|
|
207
217
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Image Classification: Shape Recognition
|
|
2
|
+
|
|
3
|
+
This example demonstrates fine-tuning SigLIP2 to classify images by dominant color patterns.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
require "fine"
|
|
9
|
+
|
|
10
|
+
Fine.configure { |c| c.progress_bar = false }
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Generate Training Data
|
|
14
|
+
|
|
15
|
+
Create synthetic training images with different colors representing different "shapes":
|
|
16
|
+
- **Circles**: Red-ish colors (RGB around 220, 80, 80)
|
|
17
|
+
- **Squares**: Green-ish colors (RGB around 80, 180, 80)
|
|
18
|
+
- **Triangles**: Blue-ish colors (RGB around 80, 80, 220)
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# Run: ruby examples/generate_shape_images.rb
|
|
22
|
+
# Creates 30 images (10 per class) in examples/data/shapes/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Train the Classifier
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
classifier = Fine::ImageClassifier.new("google/siglip2-base-patch16-224") do |config|
|
|
29
|
+
config.epochs = 5
|
|
30
|
+
config.batch_size = 4
|
|
31
|
+
config.learning_rate = 1e-4
|
|
32
|
+
config.freeze_encoder = false # Fine-tune entire model
|
|
33
|
+
|
|
34
|
+
config.on_epoch_end do |epoch, metrics|
|
|
35
|
+
puts "Epoch #{epoch}: loss=#{metrics[:loss].round(4)}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
history = classifier.fit(train_dir: "examples/data/shapes", epochs: 5)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Training Results
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Epoch 0: loss=0.8432
|
|
46
|
+
Epoch 1: loss=0.1725
|
|
47
|
+
Epoch 2: loss=0.0321
|
|
48
|
+
Epoch 3: loss=0.0027
|
|
49
|
+
Epoch 4: loss=0.0006
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The loss dropped from 0.84 to 0.0006 - a 99.9% improvement!
|
|
53
|
+
|
|
54
|
+
## Test Predictions
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
# All predictions are 100% confident and correct
|
|
58
|
+
classifier.predict("data/shapes/circle/circle_1.jpg")
|
|
59
|
+
# => [{ label: "circle", score: 1.0 }]
|
|
60
|
+
|
|
61
|
+
classifier.predict("data/shapes/square/square_1.jpg")
|
|
62
|
+
# => [{ label: "square", score: 1.0 }]
|
|
63
|
+
|
|
64
|
+
classifier.predict("data/shapes/triangle/triangle_1.jpg")
|
|
65
|
+
# => [{ label: "triangle", score: 0.999 }]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Save and Load
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
# Save
|
|
72
|
+
classifier.save("/tmp/shape-classifier")
|
|
73
|
+
|
|
74
|
+
# Load later
|
|
75
|
+
loaded = Fine::ImageClassifier.load("/tmp/shape-classifier")
|
|
76
|
+
loaded.predict("new_image.jpg")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Key Takeaways
|
|
80
|
+
|
|
81
|
+
- SigLIP2 quickly learns visual patterns even with small datasets (30 images)
|
|
82
|
+
- Fine-tuning the full model (`freeze_encoder: false`) achieves best results
|
|
83
|
+
- The model achieves perfect accuracy after just 5 epochs
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Text Embeddings: Customer Support FAQ Matching
|
|
2
|
+
|
|
3
|
+
This example demonstrates fine-tuning a sentence transformer for semantic search in a customer support context.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
require "fine"
|
|
9
|
+
|
|
10
|
+
Fine.configure { |c| c.progress_bar = false }
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Training Data Format
|
|
14
|
+
|
|
15
|
+
Create query-answer pairs in JSONL format:
|
|
16
|
+
|
|
17
|
+
```jsonl
|
|
18
|
+
{"query": "How do I reset my password?", "positive": "To reset your password, click 'Forgot Password' on the login page."}
|
|
19
|
+
{"query": "I forgot my login credentials", "positive": "To reset your password, click 'Forgot Password' on the login page."}
|
|
20
|
+
{"query": "How long does shipping take?", "positive": "Standard shipping takes 3-5 business days."}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Multiple queries can map to the same answer to teach semantic similarity.
|
|
24
|
+
|
|
25
|
+
## Train the Embedder
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
embedder = Fine::TextEmbedder.new("sentence-transformers/all-MiniLM-L6-v2") do |config|
|
|
29
|
+
config.epochs = 2
|
|
30
|
+
config.batch_size = 8
|
|
31
|
+
config.learning_rate = 2e-5
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Test pre-training similarity
|
|
35
|
+
pre_sim = embedder.similarity("How can I get my money back?", "To initiate a return, go to your orders...")
|
|
36
|
+
puts "Pre-training: #{pre_sim.round(4)}" # => 0.4008
|
|
37
|
+
|
|
38
|
+
# Fine-tune
|
|
39
|
+
history = embedder.fit(train_file: "data/support_faq_pairs.jsonl")
|
|
40
|
+
|
|
41
|
+
# Test post-training
|
|
42
|
+
post_sim = embedder.similarity("How can I get my money back?", "To initiate a return, go to your orders...")
|
|
43
|
+
puts "Post-training: #{post_sim.round(4)}" # => 0.4723
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Training Results
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Epoch 0: loss=4.3761
|
|
50
|
+
Epoch 1: loss=5.8889
|
|
51
|
+
|
|
52
|
+
Post-training similarity: 0.4723
|
|
53
|
+
Improvement: 7.15 percentage points
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Semantic Search
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
faq_corpus = [
|
|
60
|
+
"To reset your password, click 'Forgot Password' on the login page.",
|
|
61
|
+
"Standard shipping takes 3-5 business days.",
|
|
62
|
+
"To initiate a return, go to your order history and click 'Request Return'.",
|
|
63
|
+
"We accept Visa, Mastercard, American Express, PayPal, and Apple Pay.",
|
|
64
|
+
"Yes, we ship to over 50 countries.",
|
|
65
|
+
"You can reach us via live chat, email, or phone at 1-800-555-0123."
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
# Search for relevant FAQ
|
|
69
|
+
results = embedder.search("I need to get my money back", faq_corpus, top_k: 2)
|
|
70
|
+
# => [
|
|
71
|
+
# { text: "To initiate a return...", score: 0.548 },
|
|
72
|
+
# { text: "You can reach us via...", score: 0.425 }
|
|
73
|
+
# ]
|
|
74
|
+
|
|
75
|
+
results = embedder.search("What's the phone number for help?", faq_corpus)
|
|
76
|
+
# => [{ text: "You can reach us via...", score: 0.461 }]
|
|
77
|
+
|
|
78
|
+
results = embedder.search("Can you deliver to Germany?", faq_corpus)
|
|
79
|
+
# => [{ text: "Yes, we ship to over 50 countries...", score: 0.515 }]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Save and Load
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
# Save
|
|
86
|
+
embedder.save("/tmp/support-faq-embedder")
|
|
87
|
+
|
|
88
|
+
# Load later
|
|
89
|
+
loaded = Fine::TextEmbedder.load("/tmp/support-faq-embedder")
|
|
90
|
+
loaded.search("your query", corpus)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Key Takeaways
|
|
94
|
+
|
|
95
|
+
- Even 2 epochs of fine-tuning improves similarity for domain-specific queries
|
|
96
|
+
- Multiple Negatives Ranking Loss learns to distinguish relevant from irrelevant answers
|
|
97
|
+
- The model correctly identifies the best FAQ match for natural language queries
|
|
98
|
+
- Pre-trained models already provide good baseline (0.4 similarity), fine-tuning improves it
|
data/docs/quickstart.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
Get started with Fine in under 5 minutes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# macOS
|
|
9
|
+
brew install pytorch libvips
|
|
10
|
+
gem install fine
|
|
11
|
+
|
|
12
|
+
# Or add to Gemfile
|
|
13
|
+
gem 'fine'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Text Classification
|
|
17
|
+
|
|
18
|
+
Classify text into categories (sentiment, spam, intent).
|
|
19
|
+
|
|
20
|
+
**1. Prepare your data** (`reviews.jsonl`):
|
|
21
|
+
```json
|
|
22
|
+
{"text": "This product is amazing!", "label": "positive"}
|
|
23
|
+
{"text": "Terrible experience, waste of money", "label": "negative"}
|
|
24
|
+
{"text": "It's okay, nothing special", "label": "neutral"}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**2. Train and use:**
|
|
28
|
+
```ruby
|
|
29
|
+
require 'fine'
|
|
30
|
+
|
|
31
|
+
classifier = Fine::TextClassifier.new("distilbert-base-uncased")
|
|
32
|
+
classifier.fit(train_file: "reviews.jsonl", epochs: 3)
|
|
33
|
+
|
|
34
|
+
classifier.predict("Best purchase ever!")
|
|
35
|
+
# => [{ label: "positive", score: 0.95 }]
|
|
36
|
+
|
|
37
|
+
classifier.save("my_classifier")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Image Classification
|
|
43
|
+
|
|
44
|
+
Classify images into categories.
|
|
45
|
+
|
|
46
|
+
**1. Organize your images:**
|
|
47
|
+
```
|
|
48
|
+
data/
|
|
49
|
+
cats/
|
|
50
|
+
cat1.jpg
|
|
51
|
+
cat2.jpg
|
|
52
|
+
dogs/
|
|
53
|
+
dog1.jpg
|
|
54
|
+
dog2.jpg
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**2. Train and use:**
|
|
58
|
+
```ruby
|
|
59
|
+
require 'fine'
|
|
60
|
+
|
|
61
|
+
classifier = Fine::ImageClassifier.new("google/siglip2-base-patch16-224")
|
|
62
|
+
classifier.fit(train_dir: "data/", epochs: 3)
|
|
63
|
+
|
|
64
|
+
classifier.predict("test_image.jpg")
|
|
65
|
+
# => [{ label: "cat", score: 0.92 }]
|
|
66
|
+
|
|
67
|
+
classifier.save("my_image_classifier")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Text Embeddings
|
|
73
|
+
|
|
74
|
+
Generate embeddings for semantic search.
|
|
75
|
+
|
|
76
|
+
**1. Prepare training pairs** (`pairs.jsonl`):
|
|
77
|
+
```json
|
|
78
|
+
{"query": "How do I reset my password?", "positive": "Click 'Forgot Password' on the login page"}
|
|
79
|
+
{"query": "What are your hours?", "positive": "We're open Monday-Friday, 9am-5pm"}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**2. Train and use:**
|
|
83
|
+
```ruby
|
|
84
|
+
require 'fine'
|
|
85
|
+
|
|
86
|
+
embedder = Fine::TextEmbedder.new("sentence-transformers/all-MiniLM-L6-v2")
|
|
87
|
+
embedder.fit(train_file: "pairs.jsonl", epochs: 3)
|
|
88
|
+
|
|
89
|
+
# Get embeddings
|
|
90
|
+
embedding = embedder.encode("How do I change my password?")
|
|
91
|
+
|
|
92
|
+
# Semantic search
|
|
93
|
+
results = embedder.search("password help", corpus, top_k: 5)
|
|
94
|
+
|
|
95
|
+
embedder.save("my_embedder")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## LLM Fine-tuning (with LoRA)
|
|
101
|
+
|
|
102
|
+
Fine-tune language models using LoRA—only 0.5% of parameters are trainable.
|
|
103
|
+
|
|
104
|
+
**1. Prepare instruction data** (`instructions.jsonl`):
|
|
105
|
+
```json
|
|
106
|
+
{"instruction": "Summarize this text", "input": "Long article here...", "output": "Brief summary"}
|
|
107
|
+
{"instruction": "Translate to French", "input": "Hello world", "output": "Bonjour le monde"}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**2. Train with LoRA:**
|
|
111
|
+
```ruby
|
|
112
|
+
require 'fine'
|
|
113
|
+
|
|
114
|
+
# Load model and apply LoRA
|
|
115
|
+
model = Fine::Models::CausalLM.from_pretrained("google/gemma-3-1b-it")
|
|
116
|
+
Fine::LoRA.apply(model, rank: 32, alpha: 64)
|
|
117
|
+
# => "Trainable params: 5.96M (0.46%)"
|
|
118
|
+
|
|
119
|
+
# Load dataset
|
|
120
|
+
tokenizer = Fine::Tokenizers::AutoTokenizer.from_pretrained("google/gemma-3-1b-it")
|
|
121
|
+
dataset = Fine::Datasets::InstructionDataset.from_jsonl("instructions.jsonl", tokenizer: tokenizer)
|
|
122
|
+
|
|
123
|
+
# Train
|
|
124
|
+
config = Fine::LLMConfiguration.new
|
|
125
|
+
trainer = Fine::Training::LLMTrainer.new(model, config, train_dataset: dataset)
|
|
126
|
+
trainer.fit
|
|
127
|
+
|
|
128
|
+
# Merge LoRA weights and save
|
|
129
|
+
Fine::LoRA.merge!(model)
|
|
130
|
+
model.save("my_llm")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Data Formats Reference
|
|
136
|
+
|
|
137
|
+
### Text Classification
|
|
138
|
+
```json
|
|
139
|
+
{"text": "Your text here", "label": "category_name"}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Text Pairs (Embeddings)
|
|
143
|
+
```json
|
|
144
|
+
{"query": "Question text", "positive": "Matching answer"}
|
|
145
|
+
```
|
|
146
|
+
Alternative field names: `text_a`/`text_b`, `anchor`/`positive`, `sentence1`/`sentence2`
|
|
147
|
+
|
|
148
|
+
### Instructions (LLM)
|
|
149
|
+
|
|
150
|
+
**Alpaca format:**
|
|
151
|
+
```json
|
|
152
|
+
{"instruction": "Task description", "input": "Optional context", "output": "Expected response"}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**ShareGPT format:**
|
|
156
|
+
```json
|
|
157
|
+
{"conversations": [{"from": "human", "value": "Hi"}, {"from": "assistant", "value": "Hello!"}]}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Simple format:**
|
|
161
|
+
```json
|
|
162
|
+
{"prompt": "Input text", "completion": "Output text"}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Configuration
|
|
168
|
+
|
|
169
|
+
All classifiers accept a configuration block:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
Fine::TextClassifier.new("distilbert-base-uncased") do |config|
|
|
173
|
+
config.epochs = 5
|
|
174
|
+
config.batch_size = 16
|
|
175
|
+
config.learning_rate = 2e-5
|
|
176
|
+
|
|
177
|
+
config.on_epoch_end do |epoch, metrics|
|
|
178
|
+
puts "Epoch #{epoch}: loss=#{metrics[:loss]}"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Common options:
|
|
184
|
+
- `epochs` - Number of training passes (default: 3)
|
|
185
|
+
- `batch_size` - Samples per batch (default: 8-16)
|
|
186
|
+
- `learning_rate` - Learning rate (default: 2e-5)
|
|
187
|
+
- `max_length` - Max sequence length (default: 128-2048)
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Export for Production
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
# ONNX (for ONNX Runtime, TensorRT)
|
|
195
|
+
classifier.export_onnx("model.onnx")
|
|
196
|
+
|
|
197
|
+
# GGUF (for llama.cpp, Ollama)
|
|
198
|
+
llm.export_gguf("model.gguf", quantization: :q4_0)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Next Steps
|
|
204
|
+
|
|
205
|
+
- [Text Classification Tutorial](tutorials/text-classification.md)
|
|
206
|
+
- [Image Classification Tutorial](tutorials/siglip2-image-classification.md)
|
|
207
|
+
- [LLM Fine-tuning Tutorial](tutorials/llm-fine-tuning.md)
|
|
208
|
+
- [LoRA Tool Calling](tutorials/lora-tool-calling.md)
|
|
209
|
+
- [Model Export](tutorials/model-export.md)
|