BBuf's picture
Upload 2 files (#1)
417312b verified
raw
history blame contribute delete
No virus
13.7 kB
import gradio as gr
import matplotlib.pyplot as plt
import numpy as np
from descriptions import basic_texts, descriptions
def generate_data_parallel_groups(world_size, tensor_model_parallel_size, pipeline_model_parallel_size, context_parallel_size):
"""
Generate data parallel groups based on the provided parallelism parameters.
"""
assert world_size % (pipeline_model_parallel_size * tensor_model_parallel_size * context_parallel_size) == 0, "world_size must be divisible by the product of pipeline_model_parallel_size, tensor_model_parallel_size, and context_parallel_size"
data_parallel_group_ranks = []
num_pipeline_model_parallel_groups = world_size // pipeline_model_parallel_size
for i in range(pipeline_model_parallel_size):
start_rank = i * num_pipeline_model_parallel_groups
end_rank = (i + 1) * num_pipeline_model_parallel_groups
for j in range(context_parallel_size * tensor_model_parallel_size):
ranks = range(
start_rank + j, end_rank, context_parallel_size * tensor_model_parallel_size
)
data_parallel_group_ranks.append(list(ranks))
return data_parallel_group_ranks
def generate_context_data_parallel_groups(world_size, tensor_model_parallel_size, pipeline_model_parallel_size, context_parallel_size):
"""
Generate data parallel groups considering context parallelism.
"""
assert world_size % (pipeline_model_parallel_size * tensor_model_parallel_size * context_parallel_size) == 0, "world_size must be divisible by the product of pipeline_model_parallel_size, tensor_model_parallel_size, and context_parallel_size"
all_data_parallel_group_ranks_with_cp = []
num_pipeline_model_parallel_groups = world_size // pipeline_model_parallel_size
for i in range(pipeline_model_parallel_size):
start_rank = i * num_pipeline_model_parallel_groups
end_rank = (i + 1) * num_pipeline_model_parallel_groups
for j in range(tensor_model_parallel_size):
ranks_with_cp = range(start_rank + j, end_rank, tensor_model_parallel_size)
all_data_parallel_group_ranks_with_cp.append(list(ranks_with_cp))
return all_data_parallel_group_ranks_with_cp
def generate_tensor_model_parallel_groups(world_size, tensor_model_parallel_size):
"""
Generate model parallel groups based on tensor model parallel size.
"""
assert world_size % tensor_model_parallel_size == 0, "world_size must be divisible by tensor_model_parallel_size"
num_tensor_model_parallel_groups = world_size // tensor_model_parallel_size
tensor_model_parallel_group_ranks = []
for i in range(num_tensor_model_parallel_groups):
ranks = range(i * tensor_model_parallel_size, (i + 1) * tensor_model_parallel_size)
tensor_model_parallel_group_ranks.append(list(ranks))
return tensor_model_parallel_group_ranks
def generate_pipeline_parallel_groups(world_size, pipeline_model_parallel_size):
"""
Generate pipeline parallel groups based on pipeline model parallel size.
"""
assert world_size % pipeline_model_parallel_size == 0, "world_size must be divisible by pipeline_model_parallel_size"
num_pipeline_model_parallel_groups = world_size // pipeline_model_parallel_size
pipline_parallel_group_ranks = []
for i in range(num_pipeline_model_parallel_groups):
ranks = range(i, world_size, num_pipeline_model_parallel_groups)
pipline_parallel_group_ranks.append(list(ranks))
return pipline_parallel_group_ranks
def generate_context_parallel_groups(world_size, context_parallel_size, tensor_model_parallel_size, pipeline_model_parallel_size):
"""
Generate context parallel groups based on context parallel size, considering tensor and pipeline model parallel sizes.
"""
assert world_size % (context_parallel_size * tensor_model_parallel_size * pipeline_model_parallel_size) == 0, "world_size must be divisible by the product of context_parallel_size, tensor_model_parallel_size, and pipeline_model_parallel_size"
data_parallel_size = world_size // (tensor_model_parallel_size * pipeline_model_parallel_size * context_parallel_size)
context_parallel_group_ranks = []
num_pipeline_model_parallel_groups: int = world_size // pipeline_model_parallel_size
for i in range(pipeline_model_parallel_size):
for j in range(data_parallel_size):
start_rank = (
i * num_pipeline_model_parallel_groups
+ j * tensor_model_parallel_size * context_parallel_size
)
end_rank = (
i * num_pipeline_model_parallel_groups
+ (j + 1) * tensor_model_parallel_size * context_parallel_size
)
for k in range(tensor_model_parallel_size):
ranks = range(start_rank + k, end_rank, tensor_model_parallel_size)
context_parallel_group_ranks.append(list(ranks))
return context_parallel_group_ranks
def plot_parallel_groups(title="Parallel Groups", dp_groups=None, tp_groups=None, pp_groups=None, cp_groups=None):
# Initialize a figure
fig, ax = plt.subplots(figsize=(8, 6))
# Define the spacing between blocks and their size
block_size = 700 # Size of the blocks in the scatter plot
spacing = 1.5 # Spacing multiplier between blocks
if cp_groups is None:
cp_offset_x = 0
cp_offset_y = 0
tp_offset_x = 0.2
tp_offset_y = -0.2
if tp_groups:
pp_offset_x = 0.4
pp_offset_y = -0.4
else:
pp_offset_x = 0.2
pp_offset_y = -0.2
else:
cp_offset_x = 0.2
cp_offset_y = -0.2
tp_offset_x = 0.4
tp_offset_y = -0.4
if tp_groups:
pp_offset_x = 0.6
pp_offset_y = -0.6
else:
pp_offset_x = 0.4
pp_offset_y = -0.4
# Adjust the grid layout to map GPU ranks from top-left to bottom-right
num_cols = 4 # Number of columns in the grid
x_positions = np.tile(np.arange(num_cols), num_cols) * spacing
y_positions = np.repeat(np.arange(num_cols), num_cols)[::-1] * spacing # Reverse to start from top
dp_colors = plt.cm.tab20(np.linspace(0, 1, len(dp_groups)))
# 使用tab20b提高颜色区分度
if tp_groups is not None:
tp_colors = plt.cm.tab20b(np.linspace(0, 1, len(tp_groups)))
# 如果需要更多颜色,可以考虑结合使用tab20b和tab20c
if pp_groups is not None:
pp_colors = plt.cm.tab20c(np.linspace(0, 1, len(pp_groups)))
if cp_groups is not None:
cp_colors = plt.cm.tab20c(np.linspace(0, 1, len(cp_groups)))
if cp_groups is not None:
for group_idx, group in enumerate(cp_groups):
for rank in group:
x = x_positions[rank % (num_cols*num_cols)] + cp_offset_x
y = y_positions[rank % (num_cols*num_cols)] + cp_offset_y
ax.scatter(x, y, s=block_size, color=cp_colors[group_idx], edgecolor='black', zorder=5, marker='s')
ax.text(x, y, f'CP{rank}', ha='center', va='center', color='white', fontsize=8, zorder=6, fontweight='bold')
for group_idx, group in enumerate(dp_groups):
for rank in group:
x = x_positions[rank % (num_cols*num_cols)]
y = y_positions[rank % (num_cols*num_cols)]
ax.scatter(x, y, s=block_size, color=dp_colors[group_idx], edgecolor='black', zorder=5, marker='>')
ax.text(x, y, f'DP{rank}', ha='center', va='center', color='white', fontsize=8, zorder=6, fontweight='bold')
if tp_groups is not None:
for group_idx, group in enumerate(tp_groups):
for rank in group:
x = x_positions[rank % (num_cols*num_cols)] + tp_offset_x
y = y_positions[rank % (num_cols*num_cols)] + tp_offset_y
ax.scatter(x, y, s=block_size, color=tp_colors[group_idx], edgecolor='black', zorder=5, marker='p')
ax.text(x, y, f'TP{rank}', ha='center', va='center', color='white', fontsize=8, zorder=6, fontweight='bold')
if pp_groups is not None:
for group_idx, group in enumerate(pp_groups):
for rank in group:
x = x_positions[rank % (num_cols*num_cols)] + pp_offset_x
y = y_positions[rank % (num_cols*num_cols)] + pp_offset_y
ax.scatter(x, y, s=block_size, color=pp_colors[group_idx], edgecolor='black', zorder=5, marker='h')
ax.text(x, y, f'PP{rank}', ha='center', va='center', color='white', fontsize=8, zorder=6, fontweight='bold')
# Draw a separating line between Node0 and Node1
mid_y_position = np.max(y_positions) / 2
ax.axhline(y=mid_y_position, color='black', linestyle='-', linewidth=2, zorder=0)
# Add Node labels
ax.text(-spacing, max(y_positions)/4, 'Node1', verticalalignment='center', fontsize=12)
ax.text(-spacing, 3*max(y_positions)/4, 'Node0', verticalalignment='center', fontsize=12)
# Adjusting the appearance
ax.set_aspect('equal') # Keep the aspect ratio square
ax.axis('off') # Turn off the axis
plt.title(title, pad=30)
return fig
# Gradio interface setup
def create_interface():
def update_plot(parallel_group_type, tensor_model_parallel_size, pipeline_model_parallel_size, context_parallel_size, unused_text):
world_size = 16 # Fixed world size for 2 machines with 8 GPUs each
description = descriptions.get(parallel_group_type, "Invalid parallel group type")
# Initialize groups to None
data_groups = tp_groups = pp_groups = cp_groups = None
if "CP" in parallel_group_type or parallel_group_type == 'Context Parallel':
cp_groups = generate_context_parallel_groups(world_size, context_parallel_size, tensor_model_parallel_size, pipeline_model_parallel_size)
if "DP" in parallel_group_type:
data_groups = generate_context_data_parallel_groups(world_size, tensor_model_parallel_size, pipeline_model_parallel_size, context_parallel_size)
else:
if "DP" in parallel_group_type or parallel_group_type == 'Data Parallel':
data_groups = generate_data_parallel_groups(world_size, tensor_model_parallel_size, pipeline_model_parallel_size, context_parallel_size)
if parallel_group_type in ['Tensor Model Parallel', 'DP+TP', 'DP+TP+PP', 'CP+DP+TP', 'CP+DP+TP+PP']:
tp_groups = generate_tensor_model_parallel_groups(world_size, tensor_model_parallel_size)
if parallel_group_type in ['Pipeline Parallel', 'DP+PP', 'DP+TP+PP', 'CP+DP+PP', 'CP+DP+TP+PP']:
pp_groups = generate_pipeline_parallel_groups(world_size, pipeline_model_parallel_size)
# Prepare text description for display
groups_list_str = ""
if data_groups:
groups_list_str += "Data Parallel Groups:\n"
groups_list_str += "\n".join([f"Data Group {idx + 1}: {group}" for idx, group in enumerate(data_groups)])
groups_list_str += "\n--------------------------------------\n"
if tp_groups:
groups_list_str += "Tensor Model Parallel Groups:\n"
groups_list_str += "\n".join([f"Tensor Group {idx + 1}: {group}" for idx, group in enumerate(tp_groups)])
groups_list_str += "\n--------------------------------------\n"
if pp_groups:
groups_list_str += "Pipeline Model Parallel Groups:\n"
groups_list_str += "\n".join([f"Pipeline Group {idx + 1}: {group}" for idx, group in enumerate(pp_groups)])
groups_list_str += "\n--------------------------------------\n"
if cp_groups:
groups_list_str += "Context Parallel Groups:\n"
groups_list_str += "\n".join([f"Context Group {idx + 1}: {group}" for idx, group in enumerate(cp_groups)])
groups_list_str += "\n--------------------------------------\n"
text_to_display = f"==========Parallel Groups Display==========\n\n{groups_list_str}\n\n{description}"
# Generate the figure with the parallel groups
fig = plot_parallel_groups(f"{parallel_group_type} Groups", data_groups if data_groups else [], tp_groups=tp_groups, pp_groups=pp_groups, cp_groups=cp_groups)
return fig, text_to_display
iface = gr.Interface(
fn=update_plot,
inputs=[
gr.Dropdown(['Data Parallel', 'Tensor Model Parallel', 'Pipeline Parallel', 'Context Parallel',
'DP+TP', 'DP+PP', 'DP+TP+PP',
'CP+DP', 'CP+DP+TP', 'CP+DP+PP', 'CP+DP+TP+PP'], label="Parallel Group Type"),
gr.Slider(1, 8, step=1, label="Tensor Model Parallel Size"),
gr.Slider(1, 8, step=1, label="Pipeline Model Parallel Size"),
gr.Slider(1, 8, step=1, label="Context Parallel Size"),
gr.Textbox(basic_texts, interactive=False)
],
outputs=[
"plot",
"text"
],
title="Megatron-LM Parallel Group Visualization",
description="Select parallel sizes and types to visualize different parallel groups with distinct colors. This includes combinations of Data Parallel (DP), Tensor Model Parallel (TP), Pipeline Parallel (PP), and Context Parallel (CP). Note that the size of data parallelism is automatically calculated based on world_size (which is stable at 16 here) as well as tensor_model_parallel_size, pipeline_model_parallel_size, and context_parallel_size.",
live=True
)
return iface
# Create and launch the interface
iface = create_interface()
iface.launch(share=True)