Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸ“š Adding Knowledge llm blocks #50

Merged
merged 8 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,64 +15,54 @@ principles: |

Strictly follow this format for each question answer pair your generate while responding

[Start of Question]
...
[End of Question]
[Start of Response]
...
[End of Response]
[QUESTION]
<Insert question here>
[ANSWER]
<Insert answer here>
[END]


Each question and answer pair should stand alone as a mini-lesson, encapsulating a key concept or idea from the chapter in a way that is accessible and informative without requiring the reader to refer back to the textbook.

examples: |
Here are some examples of questions:

[Start of Question]
[QUESTION]
Explain the process of photosynthesis in plants. Include in your answer the roles of chlorophyll, light, water, and carbon dioxide, and describe how oxygen and glucose are produced.
[End of Question]
[Start of Response]
[ANSWER]
Photosynthesis is the process by which plants, algae, and some bacteria use sunlight to synthesize food from carbon dioxide and water. Photosynthesis in plants primarily occurs in the leaves, specifically in the chloroplasts. Chlorophyll, the green pigment in chloroplasts, absorbs light energy, which is then used to convert carbon dioxide (from the air) and water (from the soil) into glucose, a simple sugar. This process also releases oxygen as a byproduct. Light energy splits water molecules, releasing electrons and hydrogen ions and forming oxygen. The light-dependent reactions convert light energy into chemical energy (ATP and NADPH), which is used in the light-independent reactions (Calvin cycle) to convert carbon dioxide into glucose. The overall result is the conversion of solar energy into chemical energy in the form of glucose, which plants use for growth and development.
[End of Response]
[END]

[Start of Question]
[QUESTION]
In a study on the effects of temperature on enzyme activity, an enzyme exhibited its highest activity at 37Β°C. At both higher and lower temperatures, its activity decreased. Based on this information, which of the following best explains the enzyme's behavior?
Options:
a) Enzymes are temperature-sensitive and can denature at high temperatures, losing their functional shape, while at low temperatures, their reaction rates decrease due to reduced molecular motion.
b) Enzymes are more effective at higher temperatures as increased heat provides more energy for reactions, and lower temperatures cause enzymes to become more active due to enhanced molecular stability.
c) The enzyme's behavior is unrelated to temperature; instead, it is likely due to changes in pH levels, which affect enzyme activity.
d) All enzymes universally work best at exactly 37Β°C, as this is the standard temperature for all biochemical reactions in nature.
[End of Question]
[Start of Response]
[ANSWER]
a) Enzymes are temperature-sensitive and can denature at high temperatures, losing their functional shape, while at low temperatures, their reaction rates decrease due to reduced molecular motion.
[End of Response]
[END]

For this {domain} domain here are some sample questions:
[Start of Question]
{question_1}
[End of Question]
[Start of Response]
{response_1}
[End of Response]
[QUESTION]
{icl_query_1}
[ANSWER]
{icl_response_1}
[END]

[Start of Question]
{question_2}
[End of Question]
[Start of Response]
{response_2}
[End of Response]
[QUESTION]
{icl_query_2}
[ANSWER]
{icl_response_2}
[END]

[Start of Question]
{question_3}
[End of Question]
[Start of Response]
{response_3}
[End of Response]
[QUESTION]
{icl_query_3}
[ANSWER]
{icl_response_3}
[END]

generation: |
Here is the document:
{document}

generation: |
Now generate the question and answer pairs, remember to follow the principles mentioned above and use the same format as the examples. Remember to use the same style and format as the example above. Return each question between [Start of Question] and [End of Question] tags and answer between [Start of Response] and [End of Response] tags.

start_tags: ["[Start of Question]", "[Start of Response]"]
end_tags: ["[End of Question]", "[End of Response]"]
5 changes: 5 additions & 0 deletions src/instructlab/sdg/default_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ def get_flow(self) -> list:
"num_procs": 8,
"batched": True,
},
"parser_kwargs": {
"parser_name": "custom",
"parsing_pattern": r"\[(?:Question|QUESTION)\]\s*(.*?)\s*\[(?:Answer|ANSWER)\]\s*(.*?)\s*(?=\[(?:Question|QUESTION)\]|$)",
"parser_cleanup_tags": ["[END]"],
},
},
"gen_kwargs": {
"max_tokens": 2048,
Expand Down
112 changes: 97 additions & 15 deletions src/instructlab/sdg/llmblock.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Standard
from typing import Any, Dict
import re

# Third Party
Expand All @@ -14,13 +15,14 @@

class LLMBlock(Block):
# pylint: disable=too-many-instance-attributes
def __init__(

Check warning on line 18 in src/instructlab/sdg/llmblock.py

View workflow job for this annotation

GitHub Actions / lint

W0102: Dangerous default value {} as argument (dangerous-default-value)

Check warning on line 18 in src/instructlab/sdg/llmblock.py

View workflow job for this annotation

GitHub Actions / lint

W0102: Dangerous default value {} as argument (dangerous-default-value)
self,
block_name,
config_path,
client,
model_id,
output_cols,
parser_kwargs={},
aakankshaduggal marked this conversation as resolved.
Show resolved Hide resolved
model_prompt="{prompt}",
**batch_kwargs,
) -> None:
Expand All @@ -35,6 +37,9 @@
self.model_prompt = model_prompt
self.output_cols = output_cols
self.batch_params = batch_kwargs.get("batch_kwargs", {})
self.parser_name = parser_kwargs.get("parser_name", None)
aakankshaduggal marked this conversation as resolved.
Show resolved Hide resolved
self.parsing_pattern = parser_kwargs.get("parsing_pattern", None)
aakankshaduggal marked this conversation as resolved.
Show resolved Hide resolved
self.parser_cleanup_tags = parser_kwargs.get("parser_cleanup_tags", None)
aakankshaduggal marked this conversation as resolved.
Show resolved Hide resolved
self.defaults = {
"model": self.model,
"temperature": 0,
Expand All @@ -43,22 +48,38 @@

def _parse(self, generated_string) -> dict:
matches = {}
for start_tag, end_tag, output_col in zip(
self.block_config["start_tags"],
self.block_config["end_tags"],
self.output_cols,
):
if not start_tag and not end_tag:
matches[output_col] = (
generated_string.strip() if generated_string else None
)

if self.parser_name is not None and self.parser_name == "custom":
pattern = re.compile(self.parsing_pattern, re.DOTALL)
all_matches = pattern.findall(generated_string)
matches = {column_name: [] for column_name in self.output_cols}
if all_matches and isinstance(all_matches[0], tuple):
for match in all_matches:
for column_name, value in zip(self.output_cols, match):
value = value.strip()
for clean_tag in self.parser_cleanup_tags:
value = value.replace(clean_tag, "")
matches[column_name].append(value)
else:
pattern = re.escape(start_tag) + r"(.*?)" + re.escape(end_tag)
all_matches = re.findall(pattern, generated_string, re.DOTALL)
matches[output_col] = (
[match.strip() for match in all_matches] if all_matches else None
matches[self.output_cols[0]] = (
[match.strip() for match in all_matches] if all_matches else []
)

else:
for start_tag, end_tag, output_col in zip(
self.block_config["start_tags"],
self.block_config["end_tags"],
self.output_cols,
):
if not start_tag and not end_tag:
matches[output_col] = (
generated_string.strip() if generated_string else None
)
else:
pattern = re.escape(start_tag) + r"(.*?)" + re.escape(end_tag)
all_matches = re.findall(pattern, generated_string, re.DOTALL)
matches[output_col] = (
[match.strip() for match in all_matches] if all_matches else []
)
return matches

def _generate(self, samples, **gen_kwargs) -> list:
Expand Down Expand Up @@ -86,7 +107,7 @@
if (num_samples is not None) and ("num_samples" not in samples.column_names):
samples = samples.add_column("num_samples", [num_samples] * len(samples))

# validate the each sample
# validate each sample
for sample in samples:
if not self._validate(self.prompt_template, sample):
return None
Expand All @@ -107,3 +128,64 @@
new_data.append({**sample, **dict(zip(parsed_outputs.keys(), values))})

return Dataset.from_list(new_data)


class ConditionalLLMBlock(LLMBlock):
def __init__(

Check warning on line 134 in src/instructlab/sdg/llmblock.py

View workflow job for this annotation

GitHub Actions / lint

W0102: Dangerous default value {} as argument (dangerous-default-value)

Check warning on line 134 in src/instructlab/sdg/llmblock.py

View workflow job for this annotation

GitHub Actions / lint

W0102: Dangerous default value {} as argument (dangerous-default-value)
self,
block_name,
config_paths,
client,
model_id,
output_cols,
selector_column_name,
parser_kwargs={},
aakankshaduggal marked this conversation as resolved.
Show resolved Hide resolved
model_prompt="{prompt}",
**batch_kwargs,
) -> None:
super().__init__(
block_name,
config_paths[0][0],
client,
model_id,
output_cols,
parser_kwargs=parser_kwargs,
model_prompt=model_prompt,
**batch_kwargs,
)
self.selector_column_name = selector_column_name
self.prompt_template = {}
if len(config_paths) == 1 and config_paths[0][1] == "All":
self.prompt_template = self.prompt_struct.format(**self.block_config)
else:
for config, config_key in config_paths:
self.prompt_template[config_key] = self.prompt_struct.format(
**self._load_config(config)
)

def _generate(self, samples, **gen_kwargs) -> str:
if isinstance(self.prompt_template, dict):
prompts = [
self.model_prompt.format(
prompt=self.prompt_template[sample[self.selector_column_name]]
.format(**sample)
.strip()
)
for sample in samples
]
else:
prompts = [
self.model_prompt.format(
prompt=self.prompt_template.format(**sample).strip()
)
for sample in samples
]
response = self.client.completions.create(
prompt=prompts, **{**self.defaults, **gen_kwargs}
)
return [choice.text.strip() for choice in response.choices]

def _validate(self, prompt_template: str, input_dict: Dict[str, Any]) -> bool:

Check warning on line 188 in src/instructlab/sdg/llmblock.py

View workflow job for this annotation

GitHub Actions / lint

W0221: Number of parameters was 2 in 'Block._validate' and is now 3 in overriding 'ConditionalLLMBlock._validate' method (arguments-differ)

Check warning on line 188 in src/instructlab/sdg/llmblock.py

View workflow job for this annotation

GitHub Actions / lint

W0221: Number of parameters was 2 in 'Block._validate' and is now 3 in overriding 'ConditionalLLMBlock._validate' method (arguments-differ)
aakankshaduggal marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(prompt_template, dict):
prompt_template = prompt_template[input_dict[self.selector_column_name]]
return super()._validate(prompt_template, input_dict)
5 changes: 3 additions & 2 deletions src/instructlab/sdg/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def generate(self, dataset) -> Dataset:
for block_prop in self.chained_blocks:
block_type = block_prop["block_type"]
block_config = block_prop["block_config"]
drop_columns = block_prop.get("drop_columns", None)
drop_columns = block_prop.get("drop_columns", [])
gen_kwargs = block_prop.get("gen_kwargs", {})
drop_duplicates_cols = block_prop.get("drop_duplicates", False)
block = block_type(**block_config)
Expand All @@ -50,8 +50,9 @@ def generate(self, dataset) -> Dataset:

dataset = block.generate(dataset, **gen_kwargs)

drop_columns_in_ds = [e for e in drop_columns if e in dataset.column_names]
if drop_columns:
dataset = dataset.remove_columns(drop_columns)
dataset = dataset.remove_columns(drop_columns_in_ds)

if drop_duplicates_cols:
dataset = self._drop_duplicates(dataset, cols=drop_duplicates_cols)
Expand Down
12 changes: 9 additions & 3 deletions src/instructlab/sdg/utilblocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@


class SamplePopulatorBlock(Block):
def __init__(self, config_paths, column_name, **batch_kwargs) -> None:
super().__init__(block_name=self.__class__.__name__)
def __init__(self, config_paths, column_name, post_fix="", **batch_kwargs) -> None:
super().__init__(
block_name=self.__class__.__name__
) # Call the base class's __init__
self.configs = {}
for config in config_paths:
if post_fix:
config_name = config.replace(".yaml", f"_{post_fix}.yaml")
else:
config_name = config
config_key = config.split("/")[-1].split(".")[0]
self.configs[config_key] = self._load_config(config)
self.configs[config_key] = self._load_config(config_name)
self.column_name = column_name
self.num_procs = batch_kwargs.get("num_procs", 8)

Expand Down
Loading