Seyed Masoud Hosseini · Overview · Study log · Ideas · Transcript · RSS feed
Machine Learning Compilation · Lecture 6 of 8 · 47:00
Episode 6: Integration with Machine Learning Frameworks
Study guide
What this lecture covers
This lecture connects the abstractions built in earlier episodes to the outside world: how do you take a model written in an existing framework like PyTorch and bring it into the machine learning compilation flow? It answers this by introducing two building tools, the tensor expression (TE) API for generating primitive tensor functions and the BlockBuilder API for incrementally constructing a computational graph function, and then using TorchFX to trace a PyTorch model's graph and translate it, node by node, into an IRModule.
The lecture works through the running Fashion MNIST two-layer network example again, first building its IRModule by hand with BlockBuilder, then automating the process with a generic FX-to-Relax translator, and finally comparing translating operations into TensorIR calls versus higher-level Relax built-in operators. After watching, you should be able to explain what BlockBuilder and emit_te do, and how a computational graph node in one framework maps to one in another.
Key ideas
- Tensor expression (TE): a domain-specific language (
te.placeholder,te.compute) for concisely describing a tensor computation, such as matrix multiplication, that can be converted into a TensorIRPrimFuncviate.create_prim_func. - Fused TE functions:
te.computecan chain operations (for example matmul followed by ReLU) into a single generated tensor function, useful for testing or later fusion transformations. - BlockBuilder: a TVM API for incrementally constructing a Relax function, using scopes (function, dataflow block) and
emit/emit_tecalls that correspond closely to lines of generated TVMScript. emit_te: a BlockBuilder method that, given a TE function and its arguments, automatically creates the placeholder, builds the PrimFunc, inserts it into the IRModule, and emits acall_tirto it, returning aDataflowVarreferencing the result.- DataflowVar: a Relax variable that is internal to a dataflow block and not visible outside it, distinct from outputs marked visible via
emit_output. - TorchFX symbolic tracing: a PyTorch utility that captures a model's forward computation as a graph of nodes (placeholders,
call_function,call_module, get_attr, output), which can be walked in topological order for translation, though it cannot represent control flow. - Generic FX-to-Relax translation: iterating over an FX graph's nodes, mapping each PyTorch parameter to a Relax constant and each computation node to a Relax variable via a dictionary of per-operator translation functions.
- High-level Relax operators vs. TensorIR calls: an operation like matmul can be translated either into a low-level
call_tirto a generated TensorIR function, or into a high-level built-in operator likerelax.op.add, deferring the choice of implementation (library call or generated code) to a later stage.
Walkthrough
Recap and the tensor expression API (1:01)
The lecture reviews te.placeholder and te.compute, building a generic matrix multiplication function that works for any compatible shapes, and shows converting it to a TensorIR PrimFunc with te.create_prim_func and displaying it with show.
Generic and fused TE functions (7:09)
The lecture demonstrates a generic ReLU function built with a lambda that works across different tensor shapes, then shows fusing matmul and ReLU into a single TE function, with the option to keep or expose the intermediate buffer as an explicit function argument.
Building a Relax function with BlockBuilder (11:11)
The lecture introduces Relax variables as computational graph node placeholders, then uses BlockBuilder with function and dataflow-block scopes, calling emit_te to insert matmul and ReLU as call_tir operations and emit_output to mark the visible return value, producing an IRModule closely matching what was written by hand in Episode 4.
From PyTorch model to TorchFX graph (21:22)
Starting from a simple nn.Module implementing matmul plus ReLU, the lecture uses torch.fx.symbolic_trace to capture the model's computation as a graph of nodes, noting the limitation that FX tracing cannot represent control flow.
A generic FX-to-IRModule translator (24:22)
The lecture builds a from_fx function that walks the FX graph in topological order, handling placeholder nodes (model inputs), get_attr nodes (parameters, mapped to Relax constants), call_function nodes (dispatched through a user-supplied translation dictionary), and the final output node, using a node map to track FX nodes to their translated Relax variables.
Applying the translator to Fashion MNIST (36:30)
The lecture applies the translation flow to the two-layer Fashion MNIST model, defining a call_module map for nn.Linear and nn.ReLU (using TOPI's predefined topi.nn.dense tensor expression), builds the resulting IRModule, and confirms it produces the same prediction as the original PyTorch model.
High-level operators versus TensorIR calls (41:35)
The lecture shows an alternative mapping that translates PyTorch operations into Relax's built-in high-level operators (relax.op.add, relax.op.matmul) instead of generated TensorIR functions, explaining that deferring to a high-level operator keeps options open for later mapping to a library call or generated code, while direct TensorIR calls offer more flexibility for custom or fused operations.
Before you watch
- Watch Episode 4 first, since this lecture reuses its Fashion MNIST two-layer model and BlockBuilder-generated IRModule as reference points.
- Familiarity with basic PyTorch (
nn.Module,forward) is expected for the FX tracing sections. - Understanding
call_tirand dataflow blocks from Episode 4 helps when reading the BlockBuilder output.
Check your understanding
- What does
emit_tedo inside BlockBuilder, and what steps does it perform under the hood? - Why can TorchFX's symbolic tracing not represent a model containing control flow?
- How does the generic FX-to-Relax translator decide what to do with a
call_functionnode versus acall_modulenode? - What is the difference between translating an operation into a
call_tirto a TensorIR function and translating it into a high-level Relax operator likerelax.op.add? - Why does the translator need a separate
map_paramstep for handling module weights?
Chapters
- 0:00 <Untitled Chapter 1>
- 1:11 Necessary Dependencies
- 1:34 High Level Concepts
- 2:24 Tensor Expression
- 7:40 Lambda Function
- 11:32 Block Builder
- 11:40 Relaxed Variables
- 14:27 Block Builder Api
- 25:12 Map Permanent Function
- 28:37 Iterate through the Nodes
- 28:54 Placeholder
- 31:08 Translation Logic
- 31:48 Call Module
- 36:06 Customize the Translation Rules
- 46:43 Summary
From the YouTube description
In the sixth lecture for Machine Learning Compilation, CMU professor Tianqi Chen discusses how to bring ML models from existing frameworks into a ML compilation flow. The core of today's lecture will focus on getting ML models into IRModule because we can introduce more kinds of transformations on primitive functions and computation graph functions. First you learn how to use the tensor expression domain-specific language to build a TensorIR function. In order to build end-to-end model executions, we'll then need to connect multiple TensorIR functions through a computation graph. So next you will learn how to programmatically build an IRModule using the Block Builder APIs. After learning these tools, you will see how they can be used to bring a PyTorch model into IRModule format, including using TorchFX to trace a graph from the PyTorch module.
Episode 6 Notes: https://mlc.ai/chapter_integration/index.html
Episode 6 Notebook, Integration with ML Frameworks: https://github.com/mlc-ai/notebooks/blob/main/6_Integration_with_Machine_Learning_Frameworks.ipynb
What is ML Compilation?
As the first course of its kind in the world for ML compilation, in this series CMU professor Tianqi Chen introduces why AI training and inference workloads need ML compilation to transform and optimize ML models from their development state in frameworks like PyTorch and TensorFlow to their deployment form on CPUs and GPUs. MLC helps solve the problem of combinatorial explosion of ML models and deployment hardware platforms.
This course is targeted not just for for undergraduate and graduate students but also people putting ML to use - data scientists, ML engineers and hardware providers. It covers ML programming abstractions, learning-driven search, compilation, and optimized library runtimes. These themes form a new field of ML systems – machine learning compilation.
In this course, we offer the first comprehensive treatment of its kind to study key elements of this emerging field systematically. We will learn the key abstractions to represent machine learning programs, automatic optimization techniques, and approaches to optimize dependency, memory, and performance in end-to-end machine learning deployment. By completing this course, you will learn how to apply the latest developments in ML compilation to build models that can be optimized for emerging hardware stacks. This let you deploy your models efficiently - minimizing memory usage, reducing inference latency and scaling to multiple heterogeneous hardware nodes.
Full course schedule: https://mlc.ai/summer22/schedule
Instructors:
- Tianqi Chen with Hongyi Jin (TA), Siyuan Feng (TA) and Ruihang Lai (TA)
← Episode 5: Automated Program Optimization · Ep 8: GPU and Specialized Hardware, Part 2 →
