Use HiddenLayer without a GUI
masterdemos/history_canvas.py for implementation patterns.repository·master·Indexed 23 days ago
https://github.com/waleedka/hiddenlayerA lightweight library for visualizing neural network graphs and tracking training metrics (loss, accuracy, weights, activations) for PyTorch, TensorFlow, and Keras. It features Graph Expressions and Transforms to simplify complex architectures, a customizable Canvas for metric visualization, and optimization for Jupyter Notebooks and headless server environments.
demos/history_canvas.py for implementation patterns.HiddenLayer requires GraphViz and its Python wrapper to generate neural network graphs.
Using Conda:
conda install graphviz python-graphvizUsing Pip (Manual GraphViz installation required):
pip3 install graphvizconda install graphviz python-graphvizYou can install HiddenLayer using one of the following methods depending on your needs:
Use pip to install the stable version:
pip install hiddenlayerTo install the latest version directly from the GitHub repository:
pip install git+https://github.com/waleedka/hiddenlayer.gitIf you intend to edit or customize the library locally, clone the repository and install it in editable mode:
git clone git@github.com:waleedka/hiddenlayer.git
cd hiddenlayer
pip install -e .pip install hiddenlayerHiddenLayer uses Graph Expressions to find specific patterns of nodes and Transforms to modify them.
Graph Expressions act like Regular Expressions for graph structures:
Conv > Relu: Matches a Conv layer followed by a Relu layer.Conv | MaxPool: Matches Conv and MaxPool layers that are in parallel branches (sharing the same parent node).Once nodes are identified by an expression, a Transform is applied to modify the graph. Common transforms include:
hl.transforms.Fold(expression, new_name, display_name): Groups a pattern of nodes into a single node.hl.transforms.Prune(expression): Deletes nodes matching the expression.hl.transforms.FoldDuplicates(): Folds repeated patterns.To track and visualize training metrics in HiddenLayer, you use two primary abstractions:
hl.History: An object that stores metrics in RAM. You log data to it using the .log(step, **metrics) method, where step can be an integer or a tuple (e.g., (epoch, batch_ix)).hl.Canvas: An object used to draw and display the metrics stored in a History object.In a typical training loop, you log metrics to the History object at specific intervals and then call canvas.draw_plot() to visualize them.
To compare multiple experiments, you can create multiple History objects and use a with canvas: context manager to ensure multiple plots (e.g., comparing loss from two different runs) are rendered together in the same output.
# 1. Initialize
history1 = hl.History()
canvas1 = hl.Canvas()
# 2. Training loop
for step in range(800):
# ... training logic ...
if step % 10 == 0:
# Log metrics
history1.log(step, loss=loss, accuracy=accuracy)
# Draw metrics
canvas1.draw_plot([history1["loss"], history1["accuracy"]])To track training progress, use two primary classes: hl.History to store metric data and hl.Canvas to visualize it.
hl.History() to act as a data store.hl.Canvas() to handle rendering.history.log(step, metric_name=value) to record data.canvas.draw_plot([history["metric1"], history["metric2"]]) to render the metrics.import hiddenlayer as hl
history1 = hl.History()
canvas1 = hl.Canvas()
for step in range(800):
# ... training logic ...
if step % 10 == 0:
history1.log(step, loss=loss, accuracy=accuracy)
canvas1.draw_plot([history1["loss"], history1["accuracy"]])HiddenLayer uses Graph Expressions (similar to Regular Expressions) to find patterns in the model graph and Transforms to modify those patterns.
Conv > Relu: Matches a Conv layer followed by a Relu layer.Conv | MaxPool: Matches Conv and MaxPool layers that are in parallel branches (sharing the same parent node).((A > B) | C) > D: Complex nested patterns using grouping and logical operators.Once a pattern is matched, a transform can be applied. Common transforms include:
ht.Fold(expression, name, label): Groups nodes matching the expression into a single node with a specific name and display label.ht.FoldDuplicates(): Folds repeated identical nodes.ht.Prune(type): Deletes nodes of a specific type (e.g., ht.Prune("Const")).For large, repetitive architectures like ResNet, you can use hl.transforms.Fold to group complex sub-graphs into single, readable nodes. This is done by passing a list of transforms to the transforms argument in hl.build_graph.
import torch
import torchvision.models
import hiddenlayer as hl
model = torchvision.models.resnet101()
transforms = [
# Fold Conv, BN, RELU layers into one
hl.transforms.Fold("Conv > BatchNorm > Relu", "ConvBnRelu"),
# Fold Conv, BN layers together
hl.transforms.Fold("Conv > BatchNorm", "ConvBn"),
# Fold bottleneck blocks
hl.transforms.Fold("""
((ConvBnRelu > ConvBnRelu > ConvBn) | ConvBn) > Add > Relu
""", "BottleneckBlock", "Bottleneck Block"),
# Fold residual blocks
hl.transforms.Fold("""ConvBnRelu > ConvBnRelu > ConvBn > Add > Relu""",
"ResBlock", "Residual Block"),
# Fold repeated blocks
hl.transforms.FoldDuplicates(),
]
# Display graph using the transforms above
hl.build_graph(model, torch.zeros([1, 3, 224, 224]), transforms=transforms)If running on a remote server without a display, follow these steps to avoid errors:
hiddenlayer.history.progress() to print a text-based status of the metrics to the console.canvas.save("filename.png") to periodically save plots to disk for later viewing.If you are running training on a remote server without a GUI, follow these steps:
hiddenlayer. This prevents the library from attempting to open interactive windows..progress(): Call history.progress() inside your training loop to print a text-based status of the metrics to the console.canvas.save("filename.png") to periodically save snapshots of your training graphs as image files.# MUST be done BEFORE importing hiddenlayer
import matplotlib
matplotlib.use("Agg")
import hiddenlayer as hl
# In training loop
history.progress()
canvas.draw_plot([h["loss"], h["accuracy"]])
canvas.save("training_graph.png")To compare different experiments (different History objects) on the same plot, use the with canvas: context manager. This ensures that multiple draw_plot calls are rendered together in a single view. You can provide custom labels to distinguish between the metrics being compared.
history1 = hl.History()
history2 = hl.History()
canvas2 = hl.Canvas()
# ... training loops for history1 and history2 ...
# Draw comparison plots
with canvas2:
canvas2.draw_plot([history1["loss"], history2["loss"]], labels=["Loss 1", "Loss 2"])
canvas2.draw_plot([history1["accuracy"], history2["accuracy"]], labels=["Accuracy 1", "Accuracy 2"]])To visualize a Keras model using HiddenLayer, you can use hl.build_graph() by passing the Keras session graph. You can also apply transforms during the build process to group nodes into higher-level modules.
Note: Ensure you have set the Keras learning phase (K.set_learning_phase) appropriately (1 for training, 0 for inference) before building the model to ensure the graph reflects the intended state.
import tensorflow.keras.backend as K
import hiddenlayer as hl
# Set learning phase
K.set_learning_phase(0)
# Build model
model = VGG16(input_shape=(224, 224, 3))
# Build graph view
hl_graph = hl.build_graph(K.get_session().graph)
# Display
hl_graph