Add support for the Training Method for finetuning, and for Direct-Preference Optimization (DPO)#262
Add support for the Training Method for finetuning, and for Direct-Preference Optimization (DPO)#262
Conversation
src/together/utils/files.py
Outdated
| filtered_messages.append( | ||
| {column: message[column] for column in REQUIRED_COLUMNS_MESSAGE} | ||
| ) |
There was a problem hiding this comment.
Hm, I'm not sure if filtering files when they are uploaded is the right solution: this will require users to reupload their data whenever we support a new field for messages (for example, function calling)
There was a problem hiding this comment.
Agree, removed the filtering part from the function
src/together/utils/files.py
Outdated
| ) | ||
|
|
||
| if not isinstance(example["preferred_output"], list): | ||
| raise ValueError( |
There was a problem hiding this comment.
All of these should be InvalidFileFormatError
Co-authored-by: Max Ryabinin <mryabinin0@gmail.com>
Co-authored-by: Max Ryabinin <mryabinin0@gmail.com>
src/together/types/finetune.py
Outdated
| Training method type for SFT training | ||
| """ | ||
|
|
||
| method: str = "sft" |
There was a problem hiding this comment.
| method: str = "sft" | |
| method: Literal["sft"] = "sft" |
src/together/utils/files.py
Outdated
|
|
||
| has_weights = False | ||
| # Check for weights in messages | ||
| if _has_weights(messages): |
There was a problem hiding this comment.
Why did you make this into a separate function? Why not to inline it?
There was a problem hiding this comment.
It can even be like
has_weights = any("weight" in message for message in messages)
src/together/utils/files.py
Outdated
| ) | ||
| previous_role = message["role"] | ||
|
|
||
| return messages, has_weights |
There was a problem hiding this comment.
Why do you need to return messages? The row doesn't seem to be modified.
src/together/utils/files.py
Outdated
| return messages, has_weights | ||
|
|
||
|
|
||
| def validate_preference_openai(example: Dict[str, Any], idx: int = 0) -> Dict[str, Any]: |
There was a problem hiding this comment.
Why do you need to return an example?
src/together/resources/finetune.py
Outdated
| lr_scheduler_args=FinetuneLinearLRSchedulerArgs(min_lr_ratio=min_lr_ratio), | ||
| ) | ||
|
|
||
| training_method_cls: Union[TrainingMethodSFT, TrainingMethodDPO] = ( |
There was a problem hiding this comment.
Nit: since you're using the | notation to specify union types above, I would use it here as well and remove the redundant import
src/together/utils/files.py
Outdated
| has_weights = False | ||
| # Check for weights in messages | ||
| if _has_weights(messages): | ||
| has_weights = True |
There was a problem hiding this comment.
Isn't it just the following? :)
| has_weights = False | |
| # Check for weights in messages | |
| if _has_weights(messages): | |
| has_weights = True | |
| has_weights = _has_weights(messages) |
src/together/utils/files.py
Outdated
|
|
||
|
|
||
| def validate_messages( | ||
| messages: List[Dict[str, str | bool]], idx: int = 0 |
There was a problem hiding this comment.
It's hard to imagine a case where we would want to use the default line number, maybe it's best to remove the default value?
src/together/utils/files.py
Outdated
| example["input"]["messages"], _ = validate_messages( | ||
| example["input"]["messages"], idx | ||
| ) |
There was a problem hiding this comment.
We don't modify anything in messages, I would simply make validate_messages return nothing and raise an exception in case of an error
tests/unit/test_files_checks.py
Outdated
|
|
||
| def test_check_jsonl_invalid_preference_openai_structural_issues(tmp_path: Path): | ||
| # Test various structural issues in OpenAI preference format | ||
| test_cases = [ |
There was a problem hiding this comment.
Let's use pytest.mark.parametrize for iterating over multiple test cases
| def test_check_jsonl_valid_conversational_multiple_turns(tmp_path: Path): | ||
| # Create a valid JSONL file with conversational format and multiple user-assistant turn pairs | ||
| file = tmp_path / "valid_conversational_multiple_turns.jsonl" | ||
| content = [ |
There was a problem hiding this comment.
I'd prefer to keep the current file for this test and write a new one for , because
- Unit tests should test orthogonal capabilities, otherwise this gets misleading when an error is introduced (improper parsing of preference data should not affect tests for regular conversation datasets)
- Right now, it actually looks like this test is now identical to
test_check_jsonl_valid_preference_openai, which is unlikely to be what you want :)
There was a problem hiding this comment.
Created a separate file
src/together/resources/finetune.py
Outdated
| AVAILABLE_TRAINING_METHODS = { | ||
| TrainingMethodSFT().method, | ||
| TrainingMethodDPO().method, | ||
| } |
There was a problem hiding this comment.
Since this is a constant, can you move it to the top of the file (outside of the function and the class definition)?
| lr_scheduler_args=FinetuneLinearLRSchedulerArgs(min_lr_ratio=min_lr_ratio), | ||
| ) | ||
|
|
||
| training_method_cls: TrainingMethodSFT | TrainingMethodDPO = TrainingMethodSFT() |
There was a problem hiding this comment.
Nit: maybe annotate the type as training_method_cls: TrainingMethod? It's a bit clearer and more extensible
There was a problem hiding this comment.
There were some issues with pre-commit checks when I tried to do this, as I remember
There was a problem hiding this comment.
Weird, do you remember what was the error by any chance? Not blocking, but I'd love to know how to fix it in the future
tests/unit/test_preference_openai.py
Outdated
| assert report["has_min_samples"] | ||
|
|
||
|
|
||
| # Define test cases for missing fields |
tests/unit/test_preference_openai.py
Outdated
| from together.constants import MIN_SAMPLES | ||
| from together.utils.files import check_file | ||
|
|
||
| # Test data for preference OpenAI format |
There was a problem hiding this comment.
This one's also not very informative given the name of the variable
tests/unit/test_preference_openai.py
Outdated
| assert not report["is_check_passed"], f"Test should fail when {description}" | ||
|
|
||
|
|
||
| # Define test cases for structural issues |
| lr_scheduler_args=FinetuneLinearLRSchedulerArgs(min_lr_ratio=min_lr_ratio), | ||
| ) | ||
|
|
||
| training_method_cls: TrainingMethodSFT | TrainingMethodDPO = TrainingMethodSFT() |
There was a problem hiding this comment.
Weird, do you remember what was the error by any chance? Not blocking, but I'd love to know how to fix it in the future
| assert not report["is_check_passed"], f"Test should fail when {description}" | ||
|
|
||
|
|
||
| STRUCTURAL_ISSUE_TEST_CASES = [ |
There was a problem hiding this comment.
Nit: the constant can be made private
| assert report["has_min_samples"] | ||
|
|
||
|
|
||
| MISSING_FIELDS_TEST_CASES = [ |
There was a problem hiding this comment.
Nit: the constant can be made private
Describe your changes
This PR adds support for the Training Method for finetuning, and for Direct-Preference Optimization (DPO).