{
  "source_file": "0930",
  "source_platform": "Medium",
  "author": "Maxime Labonne",
  "title_listed": "GraphSAGE: Scaling up Graph Neural Networks",
  "keywords": "GNN,GraphSAGE",
  "domain": "机器学习",
  "content_type": "深度技术解析",
  "is_paid_flag": "部分免费/会员墙",
  "url": "https://towardsdatascience.com/introduction-to-graphsage-in-python-a9e7f9ecf9d7/",
  "publish_date": "2022-04",
  "corpus_stage": "预训练/SFT",
  "notes": "",
  "id": "aa246a83b15b",
  "collected_at": "2026-10-01T22:07:47",
  "title": "GraphSAGE: Scaling up Graph Neural Networks | Towards Data Science",
  "body_markdown": "![Revisit consent button](https://cdn-cookieyes.com/assets/images/revisit.svg)\nWe value your privacy\nWe use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking \"Accept All\", you consent to our use of cookies.\nCustomise Reject All Accept All\nCustomise Consent Preferences ![](https://cdn-cookieyes.com/assets/images/close.svg)\nWe use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.\nThe cookies that are categorised as \"Necessary\" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... Show more\nNecessaryAlways Active\nNecessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.\n  * Cookie\nBCTempID\n  * Duration\n10 minutes\n  * Description\nNo description available.\n\n\n  * Cookie\n__cf_bm\n  * Duration\n1 hour\n  * Description\nThis cookie, set by Cloudflare, is used to support Cloudflare Bot Management. \n\n\n  * Cookie\nAWSALBCORS\n  * Duration\n7 days\n  * Description\nAmazon Web Services set this cookie for load balancing.\n\n\n  * Cookie\n_cfuvid\n  * Duration\nsession\n  * Description\nCloudflare sets this cookie to track users across sessions to optimize user experience by maintaining session consistency and providing personalized services\n\n\n  * Cookie\nli_gc\n  * Duration\n6 months\n  * Description\nLinkedin set this cookie for storing visitor's consent regarding using cookies for non-essential purposes.\n\n\n  * Cookie\n__hssrc\n  * Duration\nsession\n  * Description\nThis cookie is set by Hubspot whenever it changes the session cookie. The __hssrc cookie set to 1 indicates that the user has restarted the browser, and if the cookie does not exist, it is assumed to be a new session.\n\n\n  * Cookie\n__hssc\n  * Duration\n1 hour\n  * Description\nHubSpot sets this cookie to keep track of sessions and to determine if HubSpot should increment the session number and timestamps in the __hstc cookie.\n\n\n  * Cookie\nwpEmojiSettingsSupports\n  * Duration\nsession\n  * Description\nWordPress sets this cookie when a user interacts with emojis on a WordPress site. It helps determine if the user's browser can display emojis properly.\n\n\n  * Cookie\nBCSessionID\n  * Duration\n1 year 1 month 4 days\n  * Description\nBlueconic sets this cookie as a unique identifier for the BlueConic profile.\n\n\n  * Cookie\n_octo\n  * Duration\n1 year\n  * Description\nNo description available.\n\n\n  * Cookie\nlogged_in\n  * Duration\n1 year\n  * Description\nNo description available.\n\n\n  * Cookie\n__Secure-YEC\n  * Duration\npast\n  * Description\nYouTube sets this cookie to stores the user's video player preferences using embedded YouTube video\n\n\n  * Cookie\n__eoi\n  * Duration\n6 months\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nAWSALBTGCORS\n  * Duration\n7 days\n  * Description\nNo description available.\n\n\n  * Cookie\nlogin-status-p\n  * Duration\npast\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nAWSALBTG\n  * Duration\n7 days\n  * Description\nNo description available.\n\n\n  * Cookie\ncsrf_token\n  * Duration\nsession\n  * Description\nNo description available.\n\n\n  * Cookie\ntoken_v2\n  * Duration\n1 day\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nD\n  * Duration\n1 year\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nPHPSESSID\n  * Duration\nsession\n  * Description\nThis cookie is native to PHP applications. The cookie stores and identifies a user's unique session ID to manage user sessions on the website. The cookie is a session cookie and will be deleted when all the browser windows are closed.\n\n\n  * Cookie\nVISITOR_PRIVACY_METADATA\n  * Duration\n6 months\n  * Description\nYouTube sets this cookie to store the user's cookie consent state for the current domain. \n\n\n  * Cookie\ncookietest\n  * Duration\nsession\n  * Description\nThe cookietest cookie is typically used to determine whether the user's browser accepts cookies, essential for website functionality and user experience.\n\n\n  * Cookie\n__Host-airtable-session\n  * Duration\n1 year\n  * Description\nThis cookie is used to enable us to integrate the services of Airtable.\n\n\n  * Cookie\n__Host-airtable-session.sig\n  * Duration\n1 year\n  * Description\nThis cookie is used to enable us to integrate the services of Airtable.\n\n\n  * Cookie\nm\n  * Duration\n1 year 1 month 4 days\n  * Description\nStripe sets this cookie for fraud prevention purposes. It identifies the device used to access the website, allowing the website to be formatted accordingly.\n\n\n  * Cookie\nBIGipServer*\n  * Duration\nsession\n  * Description\nMarketo sets this cookie to collect information about the user's online activity and build a profile about their interests to provide advertisements relevant to the user. \n\n\n  * Cookie\n__cfruid\n  * Duration\nsession\n  * Description\nCloudflare sets this cookie to identify trusted web traffic.\n\n\n  * Cookie\n_GRECAPTCHA\n  * Duration\n6 months\n  * Description\nGoogle Recaptcha service sets this cookie to identify bots to protect the website against malicious spam attacks.\n\n\n  * Cookie\n__Secure-YNID\n  * Duration\n6 months\n  * Description\nGoogle cookie used to protect user security and prevent fraud, especially during the login process.\n\n\n  * Cookie\ncookieyes-consent\n  * Duration\n1 year\n  * Description\nCookieYes sets this cookie to remember users' consent preferences so that their preferences are respected on subsequent visits to this site. It does not collect or store any personal information about the site visitors.\n\n\nFunctional\nFunctional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.\n  * Cookie\nlidc\n  * Duration\n1 day\n  * Description\nLinkedIn sets the lidc cookie to facilitate data center selection.\n\n\n  * Cookie\nbrw\n  * Duration\n1 year\n  * Description\nNo description available.\n\n\n  * Cookie\nbrwConsent\n  * Duration\n5 minutes\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nWMF-Uniq\n  * Duration\n1 year\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nloom_anon_comment\n  * Duration\n1 year\n  * Description\nNo description available.\n\n\n  * Cookie\nloom_referral_video\n  * Duration\nsession\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nVISITOR_INFO1_LIVE\n  * Duration\n6 months\n  * Description\nA cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface.\n\n\n  * Cookie\nyt-remote-connected-devices\n  * Duration\nNever Expires\n  * Description\nYouTube sets this cookie to store the user's video preferences using embedded YouTube videos.\n\n\n  * Cookie\nytidb::LAST_RESULT_ENTRY_KEY\n  * Duration\nNever Expires\n  * Description\nThe cookie ytidb::LAST_RESULT_ENTRY_KEY is used by YouTube to store the last search result entry that was clicked by the user. This information is used to improve the user experience by providing more relevant search results in the future.\n\n\n  * Cookie\nyt-remote-device-id\n  * Duration\nNever Expires\n  * Description\nYouTube sets this cookie to store the user's video preferences using embedded YouTube videos.\n\n\n  * Cookie\nyt-remote-session-name\n  * Duration\nsession\n  * Description\nThe yt-remote-session-name cookie is used by YouTube to store the user's video player preferences using embedded YouTube video.\n\n\n  * Cookie\nyt-remote-fast-check-period\n  * Duration\nsession\n  * Description\nThe yt-remote-fast-check-period cookie is used by YouTube to store the user's video player preferences for embedded YouTube videos.\n\n\n  * Cookie\nyt-remote-session-app\n  * Duration\nsession\n  * Description\nThe yt-remote-session-app cookie is used by YouTube to store user preferences and information about the interface of the embedded YouTube video player.\n\n\n  * Cookie\nyt-remote-cast-available\n  * Duration\nsession\n  * Description\nThe yt-remote-cast-available cookie is used to store the user's preferences regarding whether casting is available on their YouTube video player.\n\n\n  * Cookie\nyt-remote-cast-installed\n  * Duration\nsession\n  * Description\nThe yt-remote-cast-installed cookie is used to store the user's video player preferences using embedded YouTube video.\n\n\n  * Cookie\ncp_session\n  * Duration\n3 months\n  * Description\nCodepen sets this cookie for Help systems found in the website.\n\n\n  * Cookie\nloid\n  * Duration\n1 year 1 month 4 days\n  * Description\nThis cookie is set by the Reddit. The cookie enables the sharing of content from the website onto the social media platform.\n\n\nAnalytics\nAnalytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.\n  * Cookie\n__hstc\n  * Duration\n6 months\n  * Description\nHubspot set this main cookie for tracking visitors. It contains the domain, initial timestamp (first visit), last timestamp (last visit), current timestamp (this visit), and session number (increments for each subsequent session).\n\n\n  * Cookie\nhubspotutk\n  * Duration\n6 months\n  * Description\nHubSpot sets this cookie to keep track of the visitors to the website. This cookie is passed to HubSpot on form submission and used when deduplicating contacts.\n\n\n  * Cookie\n_ga\n  * Duration\n1 year 1 month 4 days\n  * Description\nGoogle Analytics sets this cookie to calculate visitor, session and campaign data and track site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognise unique visitors.\n\n\n  * Cookie\n_ga_*\n  * Duration\n1 year 1 month 4 days\n  * Description\nGoogle Analytics sets this cookie to store and count page views.\n\n\n  * Cookie\n__Host-psifi.analyticsTrace\n  * Duration\n6 hours\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\n__Host-psifi.analyticsTraceV2\n  * Duration\n6 hours\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\n_gh_sess\n  * Duration\nsession\n  * Description\nGitHub sets this cookie for temporary application and framework state between pages like what step the user is on in a multiple step form.\n\n\n  * Cookie\nYSC\n  * Duration\nsession\n  * Description\nYSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages.\n\n\n  * Cookie\najs_anonymous_id\n  * Duration\n1 year\n  * Description\nThis cookie is set by Segment to count the number of people who visit a certain site by tracking if they have visited before.\n\n\n  * Cookie\nvuid\n  * Duration\n1 year 1 month 4 days\n  * Description\nVimeo installs this cookie to collect tracking information by setting a unique ID to embed videos on the website. \n\n\nPerformance\nPerformance cookies are used to understand and analyse the key performance indexes of the website which helps in delivering a better user experience for the visitors.\n  * Cookie\nAWSALB\n  * Duration\n7 days\n  * Description\nAWSALB is an application load balancer cookie set by Amazon Web Services to map the session to the target.\n\n\n  * Cookie\nacq\n  * Duration\npast\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nacq.sig\n  * Duration\npast\n  * Description\nDescription is currently not available.\n\n\n  * Cookie\nptc\n  * Duration\n2 years\n  * Description\nNo description available.\n\n\nAdvertisement\nAdvertisement cookies are used to provide visitors with customised advertisements based on the pages you visited previously and to analyse the effectiveness of the ad campaigns.\n  * Cookie\nmuc_ads\n  * Duration\n1 year 1 month 4 days\n  * Description\nTwitter sets this cookie to collect user behaviour and interaction data to optimize the website.\n\n\n  * Cookie\nguest_id_marketing\n  * Duration\n1 year 1 month 4 days\n  * Description\nTwitter sets this cookie to identify and track the website visitor.\n\n\n  * Cookie\nguest_id_ads\n  * Duration\n1 year 1 month 4 days\n  * Description\nTwitter sets this cookie to identify and track the website visitor.\n\n\n  * Cookie\npersonalization_id\n  * Duration\n1 year 1 month 4 days\n  * Description\nTwitter sets this cookie to integrate and share features for social media and also store information about how the user uses the website, for tracking and targeting.\n\n\n  * Cookie\nguest_id\n  * Duration\n1 year 1 month 4 days\n  * Description\nTwitter sets this cookie to identify and track the website visitor. It registers if a user is signed in to the Twitter platform and collects information about ad preferences.\n\n\n  * Cookie\nbcookie\n  * Duration\n1 year\n  * Description\nLinkedIn sets this cookie from LinkedIn share buttons and ad tags to recognize browser IDs.\n\n\n  * Cookie\n__Secure-ROLLOUT_TOKEN\n  * Duration\n6 months\n  * Description\nYouTube sets this cookie to manage feature rollout and experimentation. It helps Google control which new features or interface changes are shown to users as part of testing and staged rollouts, ensuring consistent experience for a given user during an experiment.\n\n\n  * Cookie\nyt.innertube::nextId\n  * Duration\nNever Expires\n  * Description\nYouTube sets this cookie to register a unique ID to store data on what videos from YouTube the user has seen.\n\n\n  * Cookie\nyt.innertube::requests\n  * Duration\nNever Expires\n  * Description\nYouTube sets this cookie to register a unique ID to store data on what videos from YouTube the user has seen.\n\n\n  * Cookie\nsession_tracker\n  * Duration\nsession\n  * Description\nThis cookie is set by the Reddit. This cookie is used to identify trusted web traffic. It also helps in adverstising on the website.\n\n\n  * Cookie\nedgebucket\n  * Duration\nsession\n  * Description\nReddit sets this cookie to save the information about a log-on Reddit user, for the purpose of advertisement recommendations and updating the content.\n\n\n  * Cookie\ndid\n  * Duration\n1 year\n  * Description\nArbor sets this cookie to show targeted ads to site visitors.This cookie expires after 2 months or 1 year.\n\n\nUncategorised\nOther uncategorised cookies are those that are being analysed and have not been classified into a category as yet.\nNo cookies to display.\nReject All Save My Preferences Accept All\nToggle Search[](https://towardsdatascience.com/)Toggle Mobile Navigation\n[](https://towardsdatascience.com/)\nPublish AI, ML & data-science insights to a global community of data professionals.\nSign in[Submit an Article](https://write.insightmediagroup.io)\n[Latest](https://towardsdatascience.com/latest/)[Editor's Picks](https://towardsdatascience.com/tag/editors-pick/)[Newsletter](https://towardsdatascience.com/tag/the-variable/)[ShipAI](https://towardsdatascience.com/shipai/)\n* * *\n[Write For TDS](https://towardsdatascience.com/submissions/)\n[LinkedIn](https://www.linkedin.com/company/towards-data-science/)[X](https://x.com/TDataScience)Toggle Search\n[Programming](https://towardsdatascience.com/category/programming/)\n# GraphSAGE: Scaling up Graph Neural Networks\nIntroduction to GraphSAGE with PyTorch Geometric\n[Maxime Labonne](https://towardsdatascience.com/author/mlabonne/)\nApril 20, 202210 min read\n![Image by author, emoji by OpenMoji \\(CC BY-SA 4.0\\)](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/18TFvXqI2uTq8t-ISMu1e3Q.jpeg)\nImage by author, emoji by [OpenMoji](https://openmoji.org/) ([CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/))\nWhat do **UberEats** and **Pinterest** have in common?\nThey both use GraphSAGE **** to power their recommender system on a massive scale:** millions and billion**s of nodes and edges.\n  * 🖼 ️ **Pinterest** developed its own version called [PinSAGE](https://medium.com/pinterest-engineering/pinsage-a-new-graph-convolutional-neural-network-for-web-scale-recommender-systems-88795a107f48) to recommend the most relevant images (pins) to its users. → Their graph has 18 billion connections and 3 billion nodes.\n  * 🍽 ️ **UberEats** also reported using a [modified version of GraphSAGE](https://eng.uber.com/uber-eats-graph-learning/) to suggest dishes, restaurants, and cuisines**.** → UberEats claims to support more than 600,000 restaurants and 66 million users.\n\n\nIn this tutorial, we'll use a dataset with 20k nodes instead of billions because Google Colab cannot handle our ambitions. We will stick to the **original GraphSAGE** architecture, but the previous variants also bring exciting features we will discuss.\nYou can run the code with the following [Google Colab notebook](https://colab.research.google.com/drive/1udeUfWJzvMlLO7sGUDGsHo8cRPMicajl?usp=sharing).\nLearn this step by step with the interactive [Computer Science](https://roadmap.sh/computer-science) and [Machine Learning](https://roadmap.sh/machine-learning) roadmaps.\n## 🌐 I. PubMed dataset\n![t-SNE plot of PubMed \\(image by author\\)](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1Q2s9Pvwl596gVRDjPdJeJA.png)\nt-SNE plot of PubMed (image by author)\nIn this article, we will use the **PubMed** dataset. As we saw in the [previous article](https://towardsdatascience.com/graph-attention-networks-in-python-975736ac5c0c), PubMed is part of the [Planetoid](https://arxiv.org/abs/1603.08861) dataset (MIT license). Here's a quick summary:\n  * It contains **19,717 scientific publications** about diabetes from PubMed's database;\n  * Node features are [**TF-IDF**](https://en.wikipedia.org/wiki/Tf%E2%80%93idf)**weighted word vectors** with 500 dimensions, which is an efficient way of summarizing documents without transformers;\n  * The task is a multi-class classification with **three categories** : diabetes mellitus experimental, diabetes mellitus type 1, and diabetes mellitus type 2.\n\n\nThis is the beauty and the curse of deep learning: I don't know anything about diabetes, but I'll still feel pretty satisfied if we reach 70% accuracy. At least we're not building the next [IBM Watson](https://slate.com/technology/2022/01/ibm-watson-health-failure-artificial-intelligence.html).\npython\n\n```\nfrom torch_geometric.datasets import Planetoid\n\ndataset = Planetoid(root='.', name=\"Pubmed\")\ndata = dataset[0]\n\n# Print information about the dataset\nprint(f'Dataset: {dataset}')\nprint('-------------------')\nprint(f'Number of graphs: {len(dataset)}')\nprint(f'Number of nodes: {data.x.shape[0]}')\nprint(f'Number of features: {dataset.num_features}')\nprint(f'Number of classes: {dataset.num_classes}')\n\n# Print information about the graph\nprint(f'nGraph:')\nprint('------')\nprint(f'Training nodes: {sum(data.train_mask).item()}')\nprint(f'Evaluation nodes: {sum(data.val_mask).item()}')\nprint(f'Test nodes: {sum(data.test_mask).item()}')\nprint(f'Edges are directed: {data.is_directed()}')\nprint(f'Graph has isolated nodes: {data.has_isolated_nodes()}')\nprint(f'Graph has loops: {data.has_self_loops()}')\n```\n\ntext\n\n```\nDataset: Pubmed()\n------------------- \nNumber of graphs: 1\nNumber of nodes: 19717\nNumber of features: 500\nNumber of classes: 3\n```\n\ntext\n\n```\nGraph:\n------\nTraining nodes: 60\nEvaluation nodes: 500\nTest nodes: 1000\nEdges are directed: False\nGraph has isolated nodes: False\nGraph has loops: False\n```\n\nAs we can see, PubMed has an insanely **low number of training nodes** compared to the whole graph. There are only 60 samples to learn how to classify the 1000 test nodes.\nDespite this challenge, GNNs manage to obtain high levels of accuracy. Here's the leaderboard of known techniques (a more exhaustive benchmark can be found on [PapersWithCode](https://paperswithcode.com/sota/node-classification-on-pubmed)):\n![](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1WiHpZpQ0CpFx2tFYD-SAcw.png)\nI couldn't find any result for GraphSAGE on PubMed with this specific setting (60 training nodes, 1000 test nodes), so I don't expect a great accuracy. But another metric can be just as relevant when working with large graphs: **training time**.\n## 🧙‍♂️ II. GraphSAGE in theory\n![](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/10-cn-tQHOxyoFp9VIC8T5g.png)\n![Image by author](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1Z-dhW00goSMbqKjLrXbBUQ.png)\nImage by author\nThe GraphSAGE algorithm can be divided into two steps:\n  1. **Neighbor sampling;**\n  2. **Aggregation**.\n\n\n## 🎰 A. Neighbor sampling\nMini-batching is a common technique used in machine learning.\nIt works by **breaking down a dataset** **into smaller batches** , which allows us to train models more effectively. Mini-batching has several benefits**:**\n  1. **Improved accuracy** - mini-batches help to reduce overfitting (gradients are averaged), as well as variance in error rates;\n  2. **Increased speed** - mini-batches are processed in parallel and take less time to train than larger batches;\n  3. **Improved scalability** - an entire dataset can exceed the GPU memory, but smaller batches can get around this limitation.\n\n\nMini-batching is so useful it became standard in regular neural networks. However, it is not as straightforward with graph data, since splitting the dataset into smaller chunks would **break essential connections** between nodes.\nSo, what can we do? In recent years, researchers developed different strategies to create graph mini-batches. The one we're interested in is called [**neighbor sampling**](https://arxiv.org/abs/1706.02216). There are many other techniques you can find on [PyG's documentation](https://pytorch-geometric.readthedocs.io/en/latest/modules/loader.html), such as [subgraph clustering](https://arxiv.org/pdf/1905.07953.pdf).\n![Neighbor sampling \\(image by author\\)](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/15Oo3-SVO110a9pEs-Nkrjw.gif)\nNeighbor sampling (image by author)\nNeighbor sampling considers only a **fixed number** of random neighbors. Here's the process:\n  1. We define the **number of neighbors** (1 hop), the number of neighbors of neighbors (2 hops), etc. we would like to have.\n  2. The sampler looks at the list of neighbors, of neighbors of neighbors, etc. of a target node and **randomly selects** a predefined number of them;\n  3. The sampler **outputs a subgraph** containing the target node and the randomly selected neighboring nodes.\n\n\nThis process is **repeated for every node** in a list or the entirety of the graph. However, creating a subgraph for each node is not efficient, that is why we can process them in batches instead. In this case, each subgraph is shared by multiple target nodes.\nNeighbor sampling has an added benefit. Sometimes, we observe extremely popular nodes that act like hubs, such as celebrities on social media. Obtaining the hidden vectors of these nodes can be **computationally very expensive** since it requires calculating the hidden vectors of thousands or even millions of neighbors. GraphSAGE fixes this issue by simply ignoring most of the nodes!\nIn PyG, neighbor sampling is implemented through the `[NeighborLoader](https://pytorch-geometric.readthedocs.io/en/latest/modules/loader.html#torch_geometric.loader.NeighborLoader)` object. Let's say we want **5 neighbors and 10 of their neighbors** (`num_neighbors`). As we discussed, we can also specify a `batch_size` to speed up the process by creating subgraphs for multiple target nodes.\npython\n\n```\nfrom torch_geometric.loader import NeighborLoader\nfrom torch_geometric.utils import to_networkx\n\n# Create batches with neighbor sampling\ntrain_loader = NeighborLoader(\n    data,\n    num_neighbors=[5, 10],\n    batch_size=16,\n    input_nodes=data.train_mask,\n)\n\n# Print each subgraph\nfor i, subgraph in enumerate(train_loader):\n    print(f'Subgraph {i}: {subgraph}')\n\n# Plot each subgraph\nfig = plt.figure(figsize=(16,16))\nfor idx, (subdata, pos) in enumerate(zip(train_loader, [221, 222, 223, 224])):\n    G = to_networkx(subdata, to_undirected=True)\n    ax = fig.add_subplot(pos)\n    ax.set_title(f'Subgraph {idx}')\n    plt.axis('off')\n    nx.draw_networkx(G,\n                    pos=nx.spring_layout(G, seed=0),\n                    with_labels=True,\n                    node_size=200,\n                    node_color=subdata.y,\n                    cmap=\"cool\",\n                    font_size=10\n                    )\nplt.show()\n```\n\ntext\n\n```\nSubgraph 0: Data(x=[389, 500], edge_index=[2, 448], batch_size=16)\nSubgraph 1: Data(x=[264, 500], edge_index=[2, 314], batch_size=16)\nSubgraph 2: Data(x=[283, 500], edge_index=[2, 330], batch_size=16)\nSubgraph 3: Data(x=[189, 500], edge_index=[2, 229], batch_size=12)\n```\n\n![](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1edaTuK1oAVAzYo9bcFN11g.png)\nWe created **4 subgraphs** of various sizes. It allows us to process them in parallel and they're easier to fit on a GPU since they're smaller.\nThe number of neighbors is an important parameter since pruning our graph removes a lot of information. How much, exactly? Well, quite a lot. We can visualize this effect by looking at the **node degrees** (number of neighbors).\npython\n\n```\nfrom torch_geometric.utils import degree\nfrom collections import Counter\n\ndef plot_degree(data):\n  # Get list of degrees for each node\n  degrees = degree(data.edge_index[0]).numpy()\n\n  # Count the number of nodes for each degree\n  numbers = Counter(degrees)\n\n  # Bar plot\n  fig, ax = plt.subplots(figsize=(18, 6))\n  ax.set_xlabel('Node degree')\n  ax.set_ylabel('Number of nodes')\n  plt.bar(numbers.keys(),\n          numbers.values(),\n          color='#0A047A')\n\n# Plot node degrees from the original graph\nplot_degree(data)\n\n# Plot node degrees from the last subgraph\nplot_degree(subdata)\n```\n\n![Node degrees in the original graph](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1OUCmqikcOBad9V1u7Z6l3A.png)\nNode degrees in the original graph\n![Node degrees after neighbor sampling](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1Z0nbVfFD6kXGTzM-aIrpUw.png)\nNode degrees after neighbor sampling\nIn this example, the **maximum node degree** of our subgraphs is 5, which is much lower than the original max value. It's important to remember this tradeoff when talking about GraphSAGE.\nPinSAGE **** implements another sampling solution using** random walk**s. It has two main objectives:\n  1. Sample a **fixed number of neighbors** (like GraphSAGE);\n  2. Obtain their **relative importance** (important nodes are seen more frequently than others).\n\n\nThis strategy feels a bit like a fast **attention mechanism**. It assigns weights to nodes and increases the relevance of the most popular ones.\n## **💥 B. Aggregation**\nThe aggregation process determines how to combine the feature vectors to produce the node embeddings. The original paper presents three ways of aggregating features:\n  * **Mean** aggregator;\n  * **LSTM** aggregator;\n  * **Pooling** aggregator.\n\n\n![Aggregation \\(image by author\\)](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1dWTIARwYsjnZhpnVe5vYNQ.gif)\nAggregation (image by author)\nThe **mean aggregator** is the simplest one. The idea is close to a GCN approach:\n  1. The hidden features of the target node and its selected neighbors are averaged (Ñᵢ);\n  2. A linear transformation with a weight matrix 𝐖 is applied.\n\n\n![](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/18TAmEAlcMCB4E_vWvy7GTg.png)\nThe result can then be fed to a non-linear activation function like _ReLU_.\nThe **LSTM aggregator** can seem like a weird idea because this architecture is sequential: it assigns an order to our unordered nodes. This is why the authors randomly shuffle them to force the LSTM to only consider the hidden features. It is the best performing technique in their benchmarks.\nThe **pooling aggregator** feeds each neighbor's hidden vector to a feedforward neural network. A max-pooling operation is applied to the result.\n## 🧠 III. GraphSAGE in PyTorch Geometric\nWe can easily implement a GraphSAGE architecture in PyTorch Geometric with the `SAGEConv` layer. This implementation uses two weight matrices instead of one, like UberEats' version of GraphSAGE:\n![](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1pbxbATEdJW_LYAleH_GK8g.png)\nLet's create a network with two `SAGEConv` layers:\n  * The first one will use _**ReLU**_ as the activation function and a **dropout layer** ;\n  * The second one will directly output the **node embeddings**.\n\n\nAs we're dealing with a multi-class classification task, we'll use the cross-entropy loss as our loss function. I also added an L2 regularization of 0.0005 for good measure.\nTo see the benefits of GraphSAGE, let's **compare** it with **** a GCN and a GAT without any sampling.\ntypescript\n\n```\nclass GraphSAGE(torch.nn.Module):\n  \"\"\"GraphSAGE\"\"\"\n  def __init__(self, dim_in, dim_h, dim_out):\n    super().__init__()\n    self.sage1 = SAGEConv(dim_in, dim_h)\n    self.sage2 = SAGEConv(dim_h, dim_out)\n    self.optimizer = torch.optim.Adam(self.parameters(),\n                                      lr=0.01,\n                                      weight_decay=5e-4)\n\n  def forward(self, x, edge_index):\n    h = self.sage1(x, edge_index)\n    h = torch.relu(h)\n    h = F.dropout(h, p=0.5, training=self.training)\n    h = self.sage2(h, edge_index)\n    return h, F.log_softmax(h, dim=1)\n\n  def fit(self, data, epochs):\n    criterion = torch.nn.CrossEntropyLoss()\n    optimizer = self.optimizer\n\n    self.train()\n    for epoch in range(epochs+1):\n      acc = 0\n      val_loss = 0\n      val_acc = 0\n\n      # Train on batches\n      for batch in train_loader:\n        optimizer.zero_grad()\n        _, out = self(batch.x, batch.edge_index)\n        loss = criterion(out[batch.train_mask], batch.y[batch.train_mask])\n        acc += accuracy(out[batch.train_mask].argmax(dim=1), \n                        batch.y[batch.train_mask])\n        loss.backward()\n        optimizer.step()\n\n        # Validation\n        val_loss += criterion(out[batch.val_mask], batch.y[batch.val_mask])\n        val_acc += accuracy(out[batch.val_mask].argmax(dim=1), \n                            batch.y[batch.val_mask])\n\n      # Print metrics every 10 epochs\n      if(epoch % 10 == 0):\n          print(f'Epoch {epoch:>3} | Train Loss: {loss/len(train_loader):.3f} '\n                f'| Train Acc: {acc/len(train_loader)*100:>6.2f}% | Val Loss: '\n                f'{val_loss/len(train_loader):.2f} | Val Acc: '\n                f'{val_acc/len(train_loader)*100:.2f}%')\n```\n\nWith GraphSAGE, we loop through **batches** (our 4 subgraphs) created by the neighbor sampling process. The way we calculate the accuracy and the validation loss is also different because of that.\nHere are the results (in terms of **accuracy** and **training time**) for **** the GCN, the GAT, and GraphSAGE:\ntext\n\n```\nGCN test accuracy:        78.40% (52.6 s)\nGAT test accuracy:        77.10% (18min 7s)\nGraphSAGE test accuracy:  77.20% (12.4 s)\n```\n\nThe three models obtain **similar** results in terms of accuracy. We expect the GAT to perform better because its aggregation mechanism is more nuanced, but it's not always the case.\nThe real difference is the training time: GraphSAGE is **88 times** faster than the GAT and 4 times **** faster than the GCN in this example!\nHere lies the true power of GraphSAGE. We do lose a lot of information by pruning our graph with neighbor sampling. The final node embeddings might **not be as good** as what we could find with a GCN or a GAT. But this is not the point: GraphSAGE is designed to improve scalability. In turn, it can lead to building larger graphs that can improve accuracy.\n![Image by author](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/08VzWimEXwGrcPfNG.png)\nImage by author\nThis work was done in a supervised training setting (node classification), but we could also train GraphSAGE in an **unsupervised way**.\nIn this case, we can't use the cross-entropy loss. We have to engineer a loss function that forces nodes that are nearby in the original graph to remain close to each other in the embedding space. Conversely, the same function must ensure that **distant nodes** in the graph must have **distant representations** in the embedding space. This is the loss that is presented in [GraphSAGE's paper](https://cs.stanford.edu/people/jure/pubs/graphsage-nips17.pdf).\nIn the case of PinSAGE and UberEeats' modified GraphSAGE, we're dealing with **recommender systems**.\nThe goal is to correctly rank the most relevant items (pins, restaurants) for each user, which is very different. We don't only want to know what the closest embeddings are, we have to produce the **best rankings possible**. This is why these systems are also trained in an unsupervised way, but with another loss function: a max-margin ranking loss.\n## **Conclusion**\nGraphSAGE is an incredibly fast architecture to process large graphs. It might not be as accurate as a GCN or a GAT, but it is an essential model for handling **massive amounts of data**. It delivers this speed thanks to a clever combination of 1/ neighbor sampling to prune the graph and 2/ fast aggregation with a mean aggregator in this example. In this article,\n  * We explored a **new dataset** with PubMed, which is several times larger than the previous one;\n  * We explained the idea behind **neighbor sampling** , which only considers a predefined number of random neighbors at each hop;\n  * We saw the **three aggregators** presented in GraphSAGE's paper and focused on the mean aggregator;\n  * We benchmarked **** three models (GraphSAGE, GAT, and GCN) in terms of** accurac**y and** training tim**e.\n\n\nWe saw three architectures with the same end application: node classification. But GNNs have been successfully applied to other tasks. In the next tutorials, I'd like to use them in two different contexts: **graph and edge prediction**. This will be a good way to discover new datasets and applications where GNNs dominate the state of the art.\nIf you enjoyed this article, let's connect on Twitter [@maximelabonne](https://twitter.com/maximelabonne) for more graph learning content. Thanks for your attention! 📣\n## Next article\n> [**Chapter 4: How to Design the Most Powerful Graph Neural Network**](https://towardsdatascience.com/how-to-design-the-most-powerful-graph-neural-network-3d18b07a6e66)\n* * *\n_Learn more about machine learning and support my work with one click - become a Medium member here:_\n> [**Join Medium with my referral link - Maxime Labonne**](https://medium.com/@mlabonne/membership)\n_If you're already a member, you can_[ _follow me on Medium_](https://medium.com/@mlabonne) _._\n## Register Now to Continue Reading\n### Unlock Towards Data Science with a Free Account\nAll articles on Towards Data Science are available to registered users. Create a free account today to access this article and thousands more.\nRegister for freeSign in to your account\n* * *\n![](https://assets.insightmediagroup.io/media/1787657499317_sxy3ci.png)\nWritten By\n## [Maxime Labonne](https://towardsdatascience.com/author/mlabonne/)\n[See all from Maxime Labonne](https://towardsdatascience.com/author/mlabonne/)\n[Hands On Tutorials](https://towardsdatascience.com/tag/hands-on-tutorials/)[Machine Learning](https://towardsdatascience.com/tag/machine-learning/)[Open Source](https://towardsdatascience.com/tag/open-source/)[Programming](https://towardsdatascience.com/tag/programming/)[Python](https://towardsdatascience.com/tag/python/)\nShare This Article\n  * [](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Ftowardsdatascience.com%2Fintroduction-to-graphsage-in-python-a9e7f9ecf9d7%2F&title=GraphSAGE%3A%20Scaling%20up%20Graph%20Neural%20Networks)\n  * [](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Ftowardsdatascience.com%2Fintroduction-to-graphsage-in-python-a9e7f9ecf9d7%2F&title=GraphSAGE%3A%20Scaling%20up%20Graph%20Neural%20Networks)\n  * [](https://x.com/share?url=https%3A%2F%2Ftowardsdatascience.com%2Fintroduction-to-graphsage-in-python-a9e7f9ecf9d7%2F&text=GraphSAGE%3A%20Scaling%20up%20Graph%20Neural%20Networks)\n\n\nTowards Data Science is a community publication. Submit your insights to reach our global audience and earn through the TDS Author Payment Program.\n[Write for TDS](https://towardsdatascience.com/submissions/)\n## Related Articles\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2017/06/1KUS5ibNoTKn6i5CRYDjV8Q.jpeg)](https://towardsdatascience.com/how-i-tackled-my-first-kaggle-challenge-using-deep-learning-part-1-b0da29e1351b/)\n## [How I Tackled My First Kaggle Challenge Using Deep Learning - Part 1](https://towardsdatascience.com/how-i-tackled-my-first-kaggle-challenge-using-deep-learning-part-1-b0da29e1351b/)\n[Artificial Intelligence](https://towardsdatascience.com/category/artificial-intelligence/)\nFor the past few weeks, I have been taking the free and excellent fast.ai online course, which teaches deep learning from a practical...\n[Eddie Forson](https://towardsdatascience.com/author/ed_forson/)\nJune 10, 20176 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2019/05/1tYhC8IIUYsGAkeUyy5Gisg.png)](https://towardsdatascience.com/tcav-interpretability-beyond-feature-attribution-79b4d3610b4d/)\n## [TCAV: Interpretability Beyond Feature Attribution](https://towardsdatascience.com/tcav-interpretability-beyond-feature-attribution-79b4d3610b4d/)\n[Artificial Intelligence](https://towardsdatascience.com/category/artificial-intelligence/)\nAn overview of GoogleAI's model Interpretability technique in terms of human-friendly concepts.\n[Parul Pandey](https://towardsdatascience.com/author/pandeyparul/)\nMay 9, 20195 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2020/10/0VGGUzNOxDVAbPCe1-scaled.jpg)](https://towardsdatascience.com/deploying-your-machine-learning-apps-in-2021-a3471c049507/)\n## [Deploying Your Machine Learning Apps in 2021](https://towardsdatascience.com/deploying-your-machine-learning-apps-in-2021-a3471c049507/)\n[Artificial Intelligence](https://towardsdatascience.com/category/artificial-intelligence/)\nStreamlit Sharing is here and is awesome.\n[Arunn Thevapalan](https://towardsdatascience.com/author/arunnthevapalan/)\nOctober 18, 20204 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2024/03/0FGPo1lTwyrOjHmZB-scaled.jpg)](https://towardsdatascience.com/baseline-models-in-time-series-c76d44a826b3/)\n## [Baseline Models in Time Series](https://towardsdatascience.com/baseline-models-in-time-series-c76d44a826b3/)\n[Machine Learning](https://towardsdatascience.com/category/artificial-intelligence/machine-learning/)\nWhy (and how) you should create a baseline model before you train your final model\n[Haden Pelletier](https://towardsdatascience.com/author/pelletierhaden/)\nMarch 7, 20245 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2020/09/1laqS992KZTXbE_linokgMw.jpeg)](https://towardsdatascience.com/the-askeladden-algorithm-10859c349fc9/)\n## [The Askeladden Algorithm](https://towardsdatascience.com/the-askeladden-algorithm-10859c349fc9/)\n[Machine Learning](https://towardsdatascience.com/category/artificial-intelligence/machine-learning/)\nUsing Machine Learning to Understand Media Manipulation\n[Anna Jacobson](https://towardsdatascience.com/author/annacjacobson/)\nSeptember 16, 202011 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2020/09/1r7imryepkRtswGKzI-xHVw-scaled.jpeg)](https://towardsdatascience.com/data-analytics-to-web-app-streamlit-made-easy-ed687266f0e8/)\n## [Data Analytics to Web App: Streamlit made easy](https://towardsdatascience.com/data-analytics-to-web-app-streamlit-made-easy-ed687266f0e8/)\n[Artificial Intelligence](https://towardsdatascience.com/category/artificial-intelligence/)\nWe demonstrate how easy it is to deploy data analytics and do interactive machine learning using the Streamlit Python package.\n[Tirthajyoti Sarkar](https://towardsdatascience.com/author/tirthajyoti/)\nSeptember 3, 20209 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/01/1Jd_9mPDvsXfEvTJE2_Rk8Q.png)](https://towardsdatascience.com/manifold-alignment-c67fc3fc1a1c/)\n## [Manifold Alignment](https://towardsdatascience.com/manifold-alignment-c67fc3fc1a1c/)\n[Machine Learning](https://towardsdatascience.com/category/artificial-intelligence/machine-learning/)\nUnification of multiple datasets\n[Rahul Bhadani](https://towardsdatascience.com/author/rahulbhadani/)\nJanuary 24, 20225 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2022/01/08Z6WG427K8u1UORY-scaled.jpg)](https://towardsdatascience.com/the-science-behind-alphago-and-alphago-zero-44aeef000448/)\n## [The Science behind AlphaGo and AlphaGo Zero](https://towardsdatascience.com/the-science-behind-alphago-and-alphago-zero-44aeef000448/)\n[Deep Learning](https://towardsdatascience.com/category/artificial-intelligence/deep-learning/)\nA conceptual and friendly explanation\n[Jin Cui](https://towardsdatascience.com/author/jin-cui/)\nJanuary 31, 202213 min read\n  * [![](https://assets.insightmediagroup.io/media/wp-content/uploads/2024/11/1v6hFa8yaGNTD0DClNYl9fQ.png)](https://towardsdatascience.com/decoding-one-hot-encoding-a-beginners-guide-to-categorical-data-058582240e86/)\n## [Decoding One-Hot Encoding: A Beginner's Guide to Categorical Data](https://towardsdatascience.com/decoding-one-hot-encoding-a-beginners-guide-to-categorical-data-058582240e86/)\n[Artificial Intelligence](https://towardsdatascience.com/category/artificial-intelligence/)\nLearning to transform categorical data into a format that a machine learning model can understand\n[Slava Efimov](https://towardsdatascience.com/author/slavahead/)\nNovember 11, 20245 min read\n\n\n  * [YouTube](https://www.youtube.com/c/TowardsDataScience)\n  * [X](https://x.com/TDataScience)\n  * [LinkedIn](https://www.linkedin.com/company/towards-data-science/)\n  * [Threads](https://www.threads.net/@towardsdatascience)\n  * [Bluesky](https://bsky.app/profile/towardsdatascience.com)\n\n\n[](https://towardsdatascience.com/)\nYour home for data science and AI. The world's leading publication for data science, data analytics, data engineering, machine learning, and artificial intelligence professionals.\nSubscribe to Our Newsletter[Write For TDS](https://towardsdatascience.com/submissions/)•[About](https://towardsdatascience.com/about-towards-data-science-d691af11cc2f/)•[Advertise](https://contact.towardsdatascience.com/advertise-with-towards-data-science)•[Privacy Policy](https://towardsdatascience.com/privacy-policy/)•[Terms of Use](https://towardsdatascience.com/website-terms-of-use/)\n© Insight Media Group, LLC 2026•[The New Stack](https://thenewstack.io)•[roadmap.sh](https://roadmap.sh)•[Creator Network](https://insightmediagroup.io/creator-network)\n####  Recommended Articles \nClose\n  * [ ![](https://h030.towardsdatascience.com/rest/v2/contentStores/d7d3a4ab-7caf-4383-bbd2-fbf7fb4f1787/items/607631/image?etag=1781543525510&width=500&height=500) Critical Mistakes Companies Make When Integrating AI/ML into Their Processes ](https://towardsdatascience.com/critical-mistakes-companies-make-when-integrating-ai-ml-into-their-processes/ \"Critical Mistakes Companies Make When Integrating AI/ML into Their Processes\")\n  * [ ![](https://h030.towardsdatascience.com/rest/v2/contentStores/d7d3a4ab-7caf-4383-bbd2-fbf7fb4f1787/items/607686/image?etag=1782161464448&width=500&height=500) How Relevance Models Foreshadowed Transformers for NLP ](https://towardsdatascience.com/how-relevance-models-foreshadowed-transformers-for-nlp/ \"How Relevance Models Foreshadowed Transformers for NLP\")\n  * [ ![](https://h030.towardsdatascience.com/rest/v2/contentStores/d7d3a4ab-7caf-4383-bbd2-fbf7fb4f1787/items/607639/image?etag=1781250125848&width=500&height=500) Javascript Fatigue: HTMX Is All You Need to Build ChatGPT — Part 1 ](https://towardsdatascience.com/javascript-fatigue-you-dont-need-js-to-build-chatgpt/ \"Javascript Fatigue: HTMX Is All You Need to Build ChatGPT — Part 1\")\n  * [ ![](https://h030.towardsdatascience.com/rest/v2/contentStores/d7d3a4ab-7caf-4383-bbd2-fbf7fb4f1787/items/607930/image?etag=1782124566054&width=500&height=500) The Subset Sum Problem Solved in Linear Time for Dense Enough Inputs ](https://towardsdatascience.com/subset-sum-problem-solved-in-linear-time-for-dense-enough-inputs/ \"The Subset Sum Problem Solved in Linear Time for Dense Enough Inputs\")\n\n\n",
  "metadata": {
    "title": "GraphSAGE: Scaling up Graph Neural Networks | Towards Data Science",
    "description": "Introduction to GraphSAGE with PyTorch Geometric",
    "keywords": null,
    "author": "Maxime Labonne",
    "og:locale": "en_US",
    "og:type": "article",
    "og:title": "GraphSAGE: Scaling up Graph Neural Networks",
    "og:description": "Introduction to GraphSAGE with PyTorch Geometric",
    "og:url": "https://towardsdatascience.com/introduction-to-graphsage-in-python-a9e7f9ecf9d7/",
    "og:site_name": "Towards Data Science",
    "og:image": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/18TFvXqI2uTq8t-ISMu1e3Q.jpeg",
    "og:image:width": "1280",
    "og:image:height": "720",
    "og:image:type": "image/jpeg",
    "twitter:card": "summary_large_image",
    "twitter:site": "@TDataScience",
    "twitter:creator": "@TDataScience",
    "twitter:label1": "Written by",
    "twitter:data1": "Maxime Labonne",
    "twitter:label2": "Est. reading time",
    "twitter:data2": "10 min read",
    "article:published_time": "2022-04-20T22:09:18.000Z",
    "article:modified_time": "2022-04-20T22:09:18.000Z",
    "article:author": "https://towardsdatascience.com/author/mlabonne/"
  },
  "paywall": {
    "listed_paid": true,
    "detected": false,
    "marker": "",
    "public_only": true,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 202,
    "error": ""
  },
  "code_blocks": [
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_000.txt",
      "chars": 800
    },
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_001.txt",
      "chars": 127
    },
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_002.txt",
      "chars": 153
    },
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_003.txt",
      "chars": 926
    },
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_004.txt",
      "chars": 268
    },
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_005.txt",
      "chars": 589
    },
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_006.txt",
      "chars": 1729
    },
    {
      "language": "txt",
      "file": "attachments/aa246a83b15b/code/block_007.txt",
      "chars": 128
    }
  ],
  "images": [
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/18TFvXqI2uTq8t-ISMu1e3Q.jpeg",
      "alt": "Image by author, emoji by OpenMoji (CC BY-SA 4.0)",
      "local_path": "attachments/aa246a83b15b/images/000.jpeg",
      "bytes": 130948,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1Q2s9Pvwl596gVRDjPdJeJA.png",
      "alt": "t-SNE plot of PubMed (image by author)",
      "local_path": "attachments/aa246a83b15b/images/001.png",
      "bytes": 235734,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1Z-dhW00goSMbqKjLrXbBUQ.png",
      "alt": "Image by author",
      "local_path": "attachments/aa246a83b15b/images/002.png",
      "bytes": 154113,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/15Oo3-SVO110a9pEs-Nkrjw.gif",
      "alt": "Neighbor sampling (image by author)",
      "local_path": "attachments/aa246a83b15b/images/003.gif",
      "bytes": 509115,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1OUCmqikcOBad9V1u7Z6l3A.png",
      "alt": "Node degrees in the original graph",
      "local_path": "attachments/aa246a83b15b/images/004.png",
      "bytes": 4322,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1Z0nbVfFD6kXGTzM-aIrpUw.png",
      "alt": "Node degrees after neighbor sampling",
      "local_path": "attachments/aa246a83b15b/images/005.png",
      "bytes": 4154,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/1dWTIARwYsjnZhpnVe5vYNQ.gif",
      "alt": "Aggregation (image by author)",
      "local_path": "attachments/aa246a83b15b/images/006.gif",
      "bytes": 745838,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/04/08VzWimEXwGrcPfNG.png",
      "alt": "Image by author",
      "local_path": "attachments/aa246a83b15b/images/007.png",
      "bytes": 267403,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2017/06/1KUS5ibNoTKn6i5CRYDjV8Q.jpeg",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/008.jpeg",
      "bytes": 560924,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2019/05/1tYhC8IIUYsGAkeUyy5Gisg.png",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/009.png",
      "bytes": 1059031,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2020/10/0VGGUzNOxDVAbPCe1-scaled.jpg",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/010.jpg",
      "bytes": 391268,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2024/03/0FGPo1lTwyrOjHmZB-scaled.jpg",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/011.jpg",
      "bytes": 415021,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2020/09/1laqS992KZTXbE_linokgMw.jpeg",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/012.jpeg",
      "bytes": 37043,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2020/09/1r7imryepkRtswGKzI-xHVw-scaled.jpeg",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/013.jpeg",
      "bytes": 296020,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/01/1Jd_9mPDvsXfEvTJE2_Rk8Q.png",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/014.png",
      "bytes": 168028,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2022/01/08Z6WG427K8u1UORY-scaled.jpg",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/015.jpg",
      "bytes": 239912,
      "downloaded": true
    },
    {
      "src": "https://assets.insightmediagroup.io/media/wp-content/uploads/2024/11/1v6hFa8yaGNTD0DClNYl9fQ.png",
      "alt": "",
      "local_path": "attachments/aa246a83b15b/images/016.png",
      "bytes": 32829,
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [
    {
      "src": "https://www.youtube.com/c/TowardsDataScience",
      "quality": "unavailable",
      "local_path": "",
      "note": "无1080p且无720p，未交付更低清晰度"
    }
  ],
  "links": [
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/latest/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tag/editors-pick/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tag/the-variable/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/shipai/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/submissions/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/category/programming/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/author/mlabonne/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/graph-attention-networks-in-python-975736ac5c0c"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/how-to-design-the-most-powerful-graph-neural-network-3d18b07a6e66"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tag/hands-on-tutorials/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tag/machine-learning/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tag/open-source/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tag/programming/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tag/python/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/how-i-tackled-my-first-kaggle-challenge-using-deep-learning-part-1-b0da29e1351b/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/category/artificial-intelligence/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/author/ed_forson/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/tcav-interpretability-beyond-feature-attribution-79b4d3610b4d/"
    },
    {
      "type": "internal",
      "href": "https://towardsdatascience.com/author/pandeyparul/"
    },
    {
      "type": "external",
      "href": "https://write.insightmediagroup.io"
    },
    {
      "type": "external",
      "href": "https://www.linkedin.com/company/towards-data-science/"
    },
    {
      "type": "external",
      "href": "https://x.com/TDataScience"
    },
    {
      "type": "external",
      "href": "https://openmoji.org/"
    },
    {
      "type": "external",
      "href": "https://creativecommons.org/licenses/by-sa/4.0/"
    },
    {
      "type": "external",
      "href": "https://medium.com/pinterest-engineering/pinsage-a-new-graph-convolutional-neural-network-for-web-scale-recommender-systems-88795a107f48"
    },
    {
      "type": "external",
      "href": "https://eng.uber.com/uber-eats-graph-learning/"
    },
    {
      "type": "external",
      "href": "https://colab.research.google.com/drive/1udeUfWJzvMlLO7sGUDGsHo8cRPMicajl?usp=sharing"
    },
    {
      "type": "external",
      "href": "https://roadmap.sh/computer-science"
    },
    {
      "type": "external",
      "href": "https://roadmap.sh/machine-learning"
    },
    {
      "type": "external",
      "href": "https://arxiv.org/abs/1603.08861"
    },
    {
      "type": "external",
      "href": "https://en.wikipedia.org/wiki/Tf%E2%80%93idf"
    },
    {
      "type": "external",
      "href": "https://slate.com/technology/2022/01/ibm-watson-health-failure-artificial-intelligence.html"
    },
    {
      "type": "external",
      "href": "https://paperswithcode.com/sota/node-classification-on-pubmed"
    },
    {
      "type": "external",
      "href": "https://arxiv.org/abs/1706.02216"
    },
    {
      "type": "external",
      "href": "https://pytorch-geometric.readthedocs.io/en/latest/modules/loader.html"
    },
    {
      "type": "external",
      "href": "https://arxiv.org/pdf/1905.07953.pdf"
    },
    {
      "type": "external",
      "href": "https://cs.stanford.edu/people/jure/pubs/graphsage-nips17.pdf"
    },
    {
      "type": "external",
      "href": "https://twitter.com/maximelabonne"
    },
    {
      "type": "external",
      "href": "https://medium.com/@mlabonne/membership"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T11:11:48Z",
  "source_url": "https://towardsdatascience.com/introduction-to-graphsage-in-python-a9e7f9ecf9d7/",
  "delivery_status": "excluded_paid",
  "discovery_status": "excluded_paid",
  "missing_reason": "按原约定排除付费/会员全文"
}