所有数据均采集与网络或网友提供, 仅供学习参考使用
"mipsytipsy" / 2025-02-10 3 months ago / 未收藏/ charity.wtf发送到 kindle
I have not thought or said much about DEI (Diversity, Equity and Inclusion) over the years. Not because I don’t care about the espoused ideals — I suppose I do, rather a lot — but because corporate DEI efforts have always struck me as ineffective and bland; bolted on at best, if not actively compensating […]
"The Rust Survey Team" / 2025-02-14 3 months ago / 未收藏/ The Rust Programming Language Blog发送到 kindle
Hello, Rustaceans!
The Rust Survey Team is excited to share the results of our 2024 survey on the Rust Programming language, conducted between December 5, 2024 and December 23, 2024. As in previous years, the 2024 State of Rust Survey was focused on gathering insights and feedback from Rust users, and all those who are interested in the future of Rust more generally.
This ninth edition of the survey surfaced new insights and learning opportunities straight from the global Rust language community, which we will summarize below. In addition to this blog post, we have also prepared a report containing charts with aggregated results of all questions in the survey.
Our sincerest thanks to every community member who took the time to express their opinions and experiences with Rust over the past year. Your participation will help us make Rust better for everyone.
There's a lot of data to go through, so strap in and enjoy!

Participation

Survey Started Completed Completion rate Views
2023 11 950 9 710 82.2% 16 028
2024 9 450 7 310 77.4% 13 564
As shown above, in 2024, we have received fewer survey views than in the previous year. This was likely caused simply by the fact that the survey ran only for two weeks, while in the previous year it ran for almost a month. However, the completion rate has also dropped, which seems to suggest that the survey might be a bit too long. We will take this into consideration for the next edition of the survey.

Community

The State of Rust survey not only gives us excellent insight into how many Rust users around the world are using and experiencing the language but also gives us insight into the makeup of our global community. This information gives us a sense of where the language is being used and where access gaps might exist for us to address over time. We hope that this data and our related analysis help further important discussions about how we can continue to prioritize global access and inclusivity in the Rust community.
Same as every year, we asked our respondents in which country they live in. The top 10 countries represented were, in order: United States (22%), Germany (14%), United Kingdom (6%), France (6%), China (5%), Canada (3%), Netherlands (3%), Russia (3%), Australia (2%), and Sweden (2%). We are happy to see that Rust is enjoyed by users from all around the world! You can try to find your country in the chart below:
[PNG] [SVG]
We also asked whether respondents consider themselves members of a marginalized community. Out of those who answered, 74.5% selected no, 15.5% selected yes, and 10% preferred not to say.
We have asked the group that selected “yes” which specific groups they identified as being a member of. The majority of those who consider themselves a member of an underrepresented or marginalized group in technology identify as lesbian, gay, bisexual, or otherwise non-heterosexual. The second most selected option was neurodivergent at 46% followed by trans at 35%.
[PNG] [SVG]
Each year, we must acknowledge the diversity, equity, and inclusivity (DEI) related gaps in the Rust community and open source as a whole. We believe that excellent work is underway at the Rust Foundation to advance global access to Rust community gatherings and distribute grants to a diverse pool of maintainers each cycle, which you can learn more about here. Even so, global inclusion and access is just one element of DEI, and the survey working group will continue to advocate for progress in this domain.

Rust usage

The number of respondents that self-identify as a Rust user was quite similar to last year, around 92%. This high number is not surprising, since we primarily target existing Rust developers with this survey.
[PNG] [SVG]
Similarly as last year, around 31% of those who did not identify as Rust users cited the perception of difficulty as the primary reason for not using Rust. The most common reason for not using Rust was that the respondents simply haven’t had the chance to try it yet.
Of the former Rust users who participated in the 2024 survey, 36% cited factors outside their control as a reason why they no longer use Rust, which is a 10pp decrease from last year. This year, we also asked respondents if they would consider using Rust again if an opportunity comes up, which turns out to be true for a large fraction of the respondents (63%). That is good to hear!
Closed answers marked with N/A were not present in the previous version(s) of the survey.

Those not using Rust anymore told us that it is because they don't really need it (or the goals of their company changed) or because it was not the right tool for the job. A few reported being overwhelmed by the language or its ecosystem in general or that switching to or introducing Rust would have been too expensive in terms of human effort.
Of those who used Rust in 2024, 53% did so on a daily (or nearly daily) basis — an increase of 4pp from the previous year. We can observe an upward trend in the frequency of Rust usage over the past few years, which suggests that Rust is being increasingly used at work. This is also confirmed by other answers mentioned in the Rust at Work section later below.
[PNG] [SVG]
Rust expertise is also continually increasing amongst our respondents! 20% of respondents can write (only) simple programs in Rust (a decrease of 3pp from 2023), while 53% consider themselves productive using Rust — up from 47% in 2023. While the survey is just one tool to measure the changes in Rust expertise overall, these numbers are heartening as they represent knowledge growth for many Rustaceans returning to the survey year over year.
[PNG] [SVG]
Unsurprisingly, the most popular version of Rust is latest stable, either the most recent one or whichever comes with the users' Linux distribution. Almost a third of users also use the latest nightly release, due to various reasons (see below). However, it seems that the beta toolchain is not used much, which is a bit unfortunate. We would like to encourage Rust users to use the beta toolchain more (e.g. in CI environments) to help test soon-to-be stabilized versions of Rust.
People that use the nightly toolchain mostly do it to gain access to specific unstable language features. Several users have also mentioned that rustfmt works better for them on nightly or that they use the nightly compiler because of faster compilation times.

Learning Rust

To use Rust, programmers first have to learn it, so we are always interested in finding out how do they approach that. Based on the survey results, it seems that most users learn from Rust documentation and also from The Rust Programming Language book, which has been a favourite learning resource of new Rustaceans for a long time. Many people also seem to learn by reading the source code of Rust crates. The fact that both the documentation and source code of tens of thousands of Rust crates is available on docs.rs and GitHub makes this easier.
In terms of answers belonging to the "Other" category, they can be clustered into three categories: people using LLM (large language model) assistants (Copilot, ChatGPT, Claude, etc.), reading the official Rust forums (Discord, URLO) or being mentored while contributing to Rust projects. We would like to extend a big thank you to those making our spaces friendly and welcoming for newcomers, as it is important work and it pays off. Interestingly, a non-trivial number of people "learned by doing" and used rustc error messages and clippy as a guide, which is a good indicator of the quality of Rust diagnostics.
In terms of formal education, it seems that Rust has not yet penetrated university curriculums, as this is typically a very slowly moving area. Only a very small number of respondents (around 3%) have taken a university Rust course or used university learning materials.
[PNG] [SVG]

Programming environment

In terms of operating systems used by Rustaceans, Linux was the most popular choice, and it seems that it is getting increasingly popular year after year. It is followed by macOS and Windows, which have a very similar share of usage.
As you can see in the wordcloud, there are also a few users that prefer Arch, btw.

Rust programmers target a diverse set of platforms with their Rust programs. We saw a slight uptick in users targeting embedded and mobile platforms, but otherwise the distribution of platforms stayed mostly the same as last year. Since the WebAssembly target is quite diverse, we have split it into two separate categories this time. Based on the results it is clear that when using WebAssembly, it is mostly in the context of browsers (23%) rather than other use-cases (7%).
We cannot of course forget the favourite topic of many programmers: which IDE (developer environment) they use. Although Visual Studio Code still remains the most popular option, its share has dropped by 5pp this year. On the other hand, the Zed editor seems to have gained considerable traction recently. The small percentage of those who selected "Other" are using a wide range of different tools: from CursorAI to classics like Kate or Notepad++. Special mention to the 3 people using "ed", that's quite an achievement.
You can also take a look at the linked wordcloud that summarizes open answers to this question (the "Other" category), to see what other editors are also popular.

Rust at Work

We were excited to see that more and more people use Rust at work for the majority of their coding, 38% vs 34% from last year. There is a clear upward trend in this metric over the past few years.
[PNG] [SVG]
The usage of Rust within companies also seems to be rising, as 45% of respondents answered that their organisation makes non-trivial use of Rust, which is a 7pp increase from 2023.
[PNG] [SVG]
Once again, the top reason employers of our survey respondents invested in Rust was the ability to build relatively correct and bug-free software. The second most popular reason was Rust’s performance characteristics. 21% of respondents that use Rust at work do so because they already know it, and it's thus their default choice, an uptick of 5pp from 2023. This seems to suggest that Rust is becoming one of the baseline languages of choice for more and more companies.
[PNG] [SVG]
Similarly to the previous year, a large percentage of respondents (82%) report that Rust helped their company achieve its goals. In general, it seems that programmers and companies are quite happy with their usage of Rust, which is great!
[PNG] [SVG]
In terms of technology domains, the situation is quite similar to the previous year. Rust seems to be especially popular for creating server backends, web and networking services and cloud technologies. It also seems to be gaining more traction for embedded use-cases.
You can scroll the chart to the right to see more domains. Note that the Automotive domain was not offered as a closed answer in the 2023 survey (it was merely entered through open answers), which might explain the large jump.

It is exciting to see the continued growth of professional Rust usage and the confidence so many users feel in its performance, control, security and safety, enjoyability, and more!

Challenges

As always, one of the main goals of the State of Rust survey is to shed light on challenges, concerns, and priorities on Rustaceans’ minds over the past year.
We have asked our users about aspects of Rust that limit their productivity. Perhaps unsurprisingly, slow compilation was at the top of the list, as it seems to be a perennial concern of Rust users. As always, there are efforts underway to improve the speed of the compiler, such as enabling the parallel frontend or switching to a faster linker by default. We invite you to test these improvements and let us know if you encounter any issues.
Other challenges included subpar support for debugging Rust and high disk usage of Rust compiler artifacts. On the other hand, most Rust users seem to be very happy with its runtime performance, the correctness and stability of the compiler and also Rust's documentation.
In terms of specific unstable (or missing) features that Rust users want to be stabilized (or implemented), the most desired ones were async closures and if/let while chains. Well, we have good news! Async closures will be stabilized in the next version of Rust (1.85), and if/let while chains will hopefully follow soon after, once Edition 2024 is released (which will also happen in Rust 1.85).
Other coveted features are generators (both sync and async) and more powerful generic const expressions. You can follow the Rust Project Goals to track the progress of these (and other) features.
In the open answers to this question, people were really helpful and tried hard to describe the most notable issues limiting their productivity. We have seen mentions of struggles with async programming (an all-time favourite), debuggability of errors (which people generally love, but they are not perfect for everyone) or Rust tooling being slow or resource intensive (rust-analyzer and rustfmt). Some users also want a better IDE story and improved interoperability with other languages.
This year, we have also included a new question about the speed of Rust's evolution. While most people seem to be content with the status quo, more than a quarter of people who responded to this question would like Rust to stabilize and/or add features more quickly, and only 7% of respondents would prefer Rust to slow down or completely stop adding new features.
[PNG] [SVG]
Interestingly, when we asked respondents about their main worries for the future of Rust, one of the top answers remained the worry that Rust will become too complex. This seems to be in contrast with the answers to the previous question. Perhaps Rust users still seem to consider the complexity of Rust to be manageable, but they worry that one day it might become too much.
We are happy to see that the amount of respondents concerned about Rust Project governance and lacking support of the Rust Foundation has dropped by about 6pp from 2023.

Looking ahead

Each year, the results of the State of Rust survey help reveal the areas that need improvement in many areas across the Rust Project and ecosystem, as well as the aspects that are working well for our community.
If you have any suggestions for the Rust Annual survey, please let us know!
We are immensely grateful to those who participated in the 2024 State of Rust Survey and facilitated its creation. While there are always challenges associated with developing and maintaining a programming language, this year we were pleased to see a high level of survey participation and candid feedback that will truly help us make Rust work better for everyone.
If you’d like to dig into more details, we recommend you to browse through the full survey report.
"bang" / 2025-02-10 3 months ago / 未收藏/ bang’s blog发送到 kindle
背景 DeepSeek 里程碑式的爆火,有必要学习下是怎么回事。 大语言模型的发展,之前一直是以预训练为主,虽 […]
"Jonathan Snook <jonathan@snook.ca>" / 2025-02-10 3 months ago / 未收藏/ Snook.ca发送到 kindle
I recently subscribed to Robb Knight’s blog and came across a post on utility knives. In a small bit of serendipity, I recently rediscovered my favourite utility knife.
Having a little knife for opening up boxes and whatnot always felt like an unnecessary purchase. I usually just flip open a pair of scissors and use a side to slice through the box. It’s mildly unwieldy but sufficient.
I’ve been slowly cleaning the house, getting rid of things that aren’t needed anymore. With one kid having moved out and the other on the cusp, their toys and clothes from when they were ten no longer needed to be taking up space in the house. A collection of boxes had also accumulated under the basement stairs, many of which were placed under there when I moved into the house 11 years ago and then never touched again. You know, like that box of serial cables and phone cords. Or a box of random paper and pens that were once an office drawer quickly dumped to be moved but never sorted.
In amongst all of these artifacts was an old knife. A 30 year old knife.
A flat metal handle with a razor blade peeking from one end, wrapped in Fisher-Price stickers This is my old knife from when I used to work at Toys R Us all those years ago. To me, this is the perfect utility knife. I enjoy its simplicity. A quick tap on either end extracts or retracts the blade. New blades are easily replaced. It’s small and unassuming. And the Fisher-Price stickers give it just the right amount of je ne sais quoi.

Reply via email
"Stephan Miller" / 2025-02-10 3 months ago / 未收藏/ Codecademy Blog发送到 kindle
Preparing for a Software Developer interview can boost your confidence. This article covers common interview questions and answers to make the process easier.
The post 16 Software Developer Interview Questions and Answers appeared first on Codecademy Blog.
"Doug McNamee" / 2025-02-14 3 months ago / 未收藏/ Codecademy Blog发送到 kindle
Put your technical skills to the test and learn how to think like a developer with these coding projects for beginners.
The post 11 Coding Projects for Beginners appeared first on Codecademy Blog.
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
"Vyacheslav Egorov" / 2025-02-14 3 months ago / 未收藏/ mrale.ph发送到 kindle
2025-02-12 3 months ago / 未收藏/ HTML5 Weekly Archive Feed发送到 kindle
🚀 Frontend Focus
#​679 — February 12, 2025 | Read on the web
Taking RWD to the Extreme — It’s been 15(!) years since Ethan Marcotte first wrote about Responsive Web Design, and in that time we’ve gotten various new powerful CSS layout tools (such as Flexbox and Grid). These layout systems have in-turn created a new era of declarative, intrinsic web design where the browser is empowered to handle layouts more so than ever before.
Tomasz Jakut
🇪🇺 The European Accessibility Act for Websites and Apps — This accessibility effort comes into effect in late June, and from then apps and sites of certain companies operating in the EU must meet new a11y standards. This post does a good job outlining who it actually applies too, the requirements, and how to plan ahead.
Martijn Hols
Build Dynamic Forms with JSON-Powered Survey Creator by SurveyJS — Survey Creator is a drag-and-drop JavaScript form builder UI that auto-generates JSON form definitions, easily integrates with Angular, React, Vue, and vanilla JS, and has no backend restrictions. Perfect for form-heavy apps. Start with a free demo to learn more.
SurveyJS sponsor
How I Created a Popular WordPress Theme and Coined the Term “Hero Section” (Without Realizing It) — The story behind the recognizable ‘hero’ pattern, its name, how it caught on, and it’s wider impact — now considered a mainstream standard in web design.
Marcel Moerkens
The Popover API is Now Baseline Newly Available — Despite earlier proclamations, the Popover API (which adds a built-in declarative way to create various popovers in HTML) is now working across all devices on the latest versions of the three major browser engines.
Rachel Andrew
⚡️ IN BRIEF
📙 Articles, Opinions & Tutorials
Which Rich Text Editor Framework Should You Choose in 2025? — A round-up of actively developed WYSIWYG editor options you can drop into your apps along with the pros and cons of each.
Dexemple and Rowny (Liveblocks)
Transitioning Top-Layer Entries and the display Property — How to animate display: none and other discrete properties using CSS animation techniques (such as @starting-style and transition-behavior: allow-discrete). Lots of demos, code, and practical examples for dialogs and popovers here.
Brecht De Ruyte
You Can Now Integrate the Gemini API into Your Bolt App with a Single Prompt 🤩 — Enhancing your app with AI has never been this simple!
Bolt.new by StackBlitz sponsor
CSS Nesting: Use with Caution — “There are features that fill me with dread [..] and right at the top of that list is native CSS nesting.” — Suggests to use some caution when nesting, owing to its potential for confusion if not kept simple.
Andy Bell
How to Create Wavy Boxes Using CSS — A straightforward tutorial on how to create cool shapes we can use to decorate the border of images or any content. There's lots of code and demos here.
Temani Afif
Organizing Design System Component Patterns with CSS Cascade Layers
Ryan Trimble

WebGL Shader Techniques for Dynamic Image Transitions
Arlind Aliu

Building a Playful Stop-Motion Crayon Cursor in p5.js
Jorge Toloza

Preload Fonts on Your Site for Better Core Web Vitals
Conor McCarthy

How to Declare Your Page’s Language
Whitney Lewis

🧰 Tools, Code & Resources
Flexbox Labs: A Visual Tool for Creating Flexbox Layouts — One of the neat features is the “Layout” button that allows you to select from more than a dozen predefined layouts. There’s also a Grid version still in beta.
Praise Ogunleye
share-button: A Web Component to Share Web Pages using Native OS Sharing Options — Uses the Web Share API to tap into the OS’s underlying share mechanism. For example, on an iPhone, the share button opens a bottom sheet native to the OS.
David Darnes
Find. Fix. Test: Intro to Sentry & Codecov — Code-level visibility, from pre- to post-release, lets devs find and fix errors and slowdowns and deploy with confidence.
Sentry sponsor
AstroPaper: Minimal, Responsive, SEO-Friendly Astro Blog Theme — A well put together blog theme for the Astro content-driven site Web framework.
Sat Naing
Visprex: An Online Tool to Visualize CSV Files — It’s open source and works entirely in the browser, so you can install it locally. The demo site lets you load examples which can display as a histogram, scatter plot, line plot, or correlation matrix.
visprex
PostSpark: Customize and Beautify Website & Code Screenshots — These kinds of tools are a dime a dozen, but this one’s pretty nice. It lets you generate the screenshot content directly from a link (X, Bluesky, GitHub, etc.), and has lots of ways to customize.
PostSpark
FakeData: A Simple App for Generating Fake JSON Data — You can choose from 11 categories (users, orders, payments, etc.), after which you can edit/remove fields then copy or download the JSON data for use in your app.
FakeData
📰 Classifieds
🎹 STRICH: Add blazing fast and reliable 1D/2D Barcode Scanning to your web apps. Free demo app and 30-day trial available.
Telling the Bit story: Celebrating 10 Years of Composability. Ran Mizrahi reveals how Bit shifted from development stagnation to exponential progress using Composability.
Bad PDFs drive users away with slow load times, broken annotations, and clunky UX. Nutrient’s PDF SDKs, used by 10K+ devs, give seamless document experiences, with over 100 features.
🤔 ...and finally
🤖 The 70% Problem: Hard Truths About AI-Assisted Coding — Addy shared this late last year, but I missed it at the time. He’s spent the past few years working in AI-assisted development, and has spotted a few things worth sharing that “we need to reckon with”. This is a good overview of the current state of AI, highlighting its proficiency at prototyping, where it struggles, and how devs can best leverage it. A must-read if you’re navigating similar waters.
Addy Osmani
Ipx.
"王巍 (onevcat)" / 2025-02-14 3 months ago / 未收藏/ OneV's Den发送到 kindle
"juan.martinez@okta.com" / 2025-02-13 3 months ago / 未收藏/ Auth0 Blog发送到 kindle
Learn how to use Okta FGA to secure your LangChain RAG agent in Python.
"Samantha Murphy" / 2025-02-13 3 months ago / 未收藏/ Auth0 Blog发送到 kindle
If you’re curious about how to stay ahead of increasingly sophisticated attacks without putting your business goals at risk with excessive friction, this resource is for you.
"jacquelyn.painter@okta.com" / 2025-02-13 3 months ago / 未收藏/ Auth0 Blog发送到 kindle
Empower your team with a unified digital Identity strategy that includes intelligent, integrated auth capabilities that minimize risk while improving experience.
"jacquelyn.painter@okta.com" / 2025-02-13 3 months ago / 未收藏/ Auth0 Blog发送到 kindle
To meet innovation, experience, and security requirements, organizations must establish digital Identity strategies and implement them into every product and service.
"williamlong" / 2025-02-10 3 months ago / 未收藏/ 月光博客发送到 kindle
中医是中国传统的医学体系,经过几千年的发展,形成了自己独特的理论体系和治疗方法,中医核心理论包括阴阳五行、脏腑气血等概念,这些理论深深植根于中国古代的哲学和自然观中,然而,这些理论并不符合现代科学的标准,因此不能被视为科学理论。本文将从多个角度阐述为何中医的理论基础不是科学理论。
一、中医的核心理论
中医的核心理论包括阴阳五行学说、气血学说、脏腑学说等,这些理论贯穿了中医学的各个方面,指导着中医的诊断和治疗。
1. 阴阳学说
阴阳指的是,宇宙间的一切事物根据其属性可分为两类,阴类和阳类。例如,白天和夜晚、寒和热、内和外等都可以用阴阳来解释。
五行指的是,宇宙万物最基础的五种物质:金、木、水、火、土。五行说将自然界一切事物的性质纳入这五类的范畴。
阴阳互相依存,互相转化。五行相生相克,相互制约、相互生成。
中医认为阴阳五行理论能够解释人体的生理活动和疾病的发生,通过调节阴阳、五行、气血、脏腑等达到恢复身体平衡的目的。比如,若人体的“阳气”不足,可能会表现为寒冷、疲乏等症状;而若“阴气”过多,则可能导致体内湿气积聚、体重增加等问题。
2. 气血学说
气血指的是人体内外的生命能量和动态过程。
气是人体内的生命动力,充满了全身各个组织和器官。气分为正气(如元气)和邪气(如外感风寒)两种,正气能够保卫人体健康,而邪气则是导致疾病的原因。
血类似于现代医学中的血液。
中医认为,通过调节气血,能够改善机体的功能,治疗疾病。比如,气血不足可能导致面色苍白、乏力、脉象虚弱等症状。
3. 脏腑学说
脏主要指内脏器官(心、肝、脾、肺、肾),腑指的是消化器官(胆、胃、大肠、小肠、膀胱)。
脏腑学说认为,五脏不仅有其各自的生理功能,还分别与五行理论中的木、火、土、金、水相对应。例如,肝主疏泄,心主血脉,脾主运化,肺主气,肾主水。六腑负责接收、消化和排泄食物等物质,它们和脏器一起共同作用,维持人体的正常运作。脏腑之间的协作和相互制约是中医诊疗的关键。
4. 经络学说
经络是经脉和络脉的总称,是运行气血、联系脏腑和体表及全身各部的通道,是人体功能的调控系统,起到调节气血、调和阴阳、维护身体平衡的作用。
十二经脉是经络系统的主体,与五脏对应的经脉为阴经,分布于四肢内侧和胸腹;与六腑对应的经脉称为阳经,分布于四肢外侧和头面、躯干。
以上是中医核心理论的简述,下面讲一下科学理论的简述。
二、科学理论的核心特征
科学是一个建立在可检验的解释和对客观事物的形式、组织等进行预测的有序知识系统,是已系统化和公式化了的知识。
科学理论是指通过严谨的实验、观察、推理以及系统化的验证过程,得出的关于自然界或社会现象的普遍性解释。它要求能够提出可验证、可重复的假设,且能够通过实验数据或实证研究加以证实或证伪。
卡尔·波普尔认为,科学的本质是“可证伪性”,所有科学理论都有可证伪性,不可证伪的理论不是科学理论。
可证伪性,是指“可以被证明是错的”,科学的理论一般都是“可证伪、且未被证伪”的。例如“特定西药A在治疗特定疾病B时,其有效率超过80%”是可证伪的命题,而“阴阳平衡是健康的基础”则无法通过实验证伪。
然而,中医的核心理论——阴阳五行、气血经络,恰恰缺乏上述科学理论所必须的特征。它们更像是抽象的哲学概念和经验总结,而不是基于实证和实验的科学理论。
三、中医理论的非科学本质
阴阳五行的核心思想更接近哲学思辨和象征性解释,而非可实验验证的科学理论。它们没有具体的量化标准,且难以通过实验来验证其正确性。例如,如何定量测量一个人的“阴阳”是否平衡?如何在实验室中证明五行的相生相克规律?这些都超出了科学范畴,无法通过科学实验来验证。
中医认为元素是由阴类和阳类组成的,实际上,元素由质子、中子和电子组成。中医认为基本元素为金、木、水、火、土,实际上,宇宙中最多的元素分别是氢、氦、氧、碳、氖、铁、氮、硅、镁、硫等,目前已经发现了118种元素。
中医的阴阳既可指寒热、动静等物理属性,也可指虚实、表里等抽象状态,这种开放性使其能解释任何现象,因此无法证伪。
实际上,阴阳五行的核心思想更接近哲学思辨和象征性解释,而非可实验验证的科学理论。它们没有具体的量化标准,且难以通过实验来验证其正确性。
气的具体定义和测量方式非常模糊,无法用现代物理学中的“能量”或“力”来描述,也没有明确的物理单位来量化。气的存在无法通过现有的科学仪器或实验方法直接观察或测量。
血在中医中指的是携带养分和能量的物质,类似于现代医学中的血液,但血液的流动、成分及其功能在现代医学中有着更加明确的定义和研究。
经络的存在并没有被现代医学证实,解剖学从未发现独立于血管、神经系统的“经络”实体,因此西方主流医学界认为经络是不存在的。
中医的脏腑学说强调脏腑功能的协调与平衡,如肝藏血、心主血脉、脾运化、肺主气、肾主水等,并通过调节这些功能来维持人体的健康。这些概念在中医的临床实践中起到了重要作用,但它们并不符合科学理论的标准,因为它们无法量化、无法用实验验证,也无法明确说明其具体的物理基础。
现代医学的脏腑学说则是基于解剖学和生理学的视角,侧重于每个器官的具体结构与功能。现代医学将脏腑视为具有具体功能的生理器官,现代医学对于器官的理解更具科学性和可量化性,器官的功能通常可以通过实验、影像学检查等手段明确。
可见,中医理论绝大多数都是类似传统哲学范畴的理论,无法通过现代科学的方式进行验证。
此外,中医药的治疗方法往往基于经验积累,对于草药的药物原理基本一窍不通,无法消除药物的副作用。例如,古代中医发现柳树皮可退烧,但用“清热解毒”解释纯属错误归因。柳树皮的不良反应很明显,会引起恶心、呕吐和耳鸣等不良反应,还会诱发胃溃疡甚至是胃出血,西医则从柳树皮里提取出水杨酸,将水杨酸上的羟基,与乙酸酐反应,形成乙酰化的水杨酸,即乙酰水杨酸:阿司匹林,阿司匹林对胃部的刺激大幅降低,很容易被肠道吸收并进入血液,成为当今世界上应用最广泛的药物之一。
四、结论
中医的这些理论大多源于古代哲学和经验总结,具有高度的抽象性和象征性,而缺乏可验证性、可量化性和普遍适用性,这些理论与现代科学的生物学、化学、物理学等学科体系有着明显的不同,更多的是一种哲学性和经验性的认识,体现了古代中国人对自然和生命的独特看法,这些理论缺乏现代科学的验证,并不是科学理论。
中医理论的真正定位应该仅限于文化、历史和哲学范畴,部分中医药疗法可纳入经验医学领域,但需剥离玄学解释。
​一直以来,如何科学、有效的评价中药的疗效,中药是否应当以双盲临床对照试验的方式来证明疗效,是医学界广泛讨论的话题。事实上,越来越多的中药也在进行随机、双盲、多中心平行对照临床试验,中药这种适应时代变化的行为都是有正面意义的。
总而言之,只有承认中医理论基础的非科学性,才能避免其沦为反智主义的温床,并为传统医学的现代化开辟理性道路。只有区分出中医的文化价值、实践价值和科学验证的边界,才能探索中医的科学化和现代化的发展路径。
"Brigit Murtaugh, Burke Holland" / 2025-02-14 3 months ago / 未收藏/ Visual Studio Code - Code Editing. Redefined.发送到 kindle
Announcing the Next Edit Suggestions and Agent Mode for GitHub Copilot in Visual Studio Code.
Read the full article
2025-02-08 3 months ago / 未收藏/ egghead.io - Bite-sized Web Development Video Tutorials & Training发送到 kindle
AI is amazing for generating code, but often leaves us staring at functions and libraries we've never seen before. This creates a huge problem: When things break (and they will break), we're lost. We don't understand the individual pieces, let alone how they interact. Knowing one part isn't enough; we need to see how unfamiliar components work together. The solution? Real-world examples. This lesson introduces ghx, a tool I built to do exactly that. ghx searches GitHub, finding actual code examples that use the specific combinations of functions and libraries that are tripping you up. Instead of generic documentation, you get concrete implementations. You can find the ghx repository at https://github.com/johnlindquist/ghx. Here's the workflow we cover: Identify the Unknown: Pinpoint the AI-generated code you don't understand – specific functions, libraries, or their interactions. Search with ghx: Use ghx to search GitHub for examples using those terms together. ghx finds repos where those elements co-exist. Consolidate Examples: ghx combines the relevant code snippets from those examples into a single, digestible file. Ask the AI (Again, but Better!): Feed this consolidated code to an AI (like a coding-focused model in an AI studio). Now, instead of asking general questions, you can ask targeted questions based on real-world usage: "Explain how these functions are used together in these examples." "What are some common use cases for this combination of libraries?" "Show me alternative code snippets that achieve the same result." "Can you identify potential problems or areas for improvement, based on these examples?" Iterate. Use your improved understanding to make changes, and then repeat the process to debug. By leveraging ghx and targeted AI prompting, we transform from being lost in a sea of unfamiliar code to actively learning from how others have solved similar problems. We gain a deeper understanding of our AI-generated codebase, making debugging, modification, and future development far more manageable. This gives you the tools to understand, debug and improve your own code.
2025-02-12 3 months ago / 未收藏/ egghead.io - Bite-sized Web Development Video Tutorials & Training发送到 kindle
Install `ghx` with `npm i -g @johnlindquist/ghx` https://github.com/johnlindquist/ghx You've copied and pasted code, set up your project, and... *error*. "Stream is not readable." Sound familiar? Generic Google searches and outdated Stack Overflow answers often leave you more confused than when you started. You need more than just documentation; you need *context*. You need to see how real developers solve this *exact* problem. This lesson shows you a better way to debug, using real-world GitHub examples and the power of Cursor's Composer. Here's the workflow we'll cover: 1. **Identify the Error:** You've already done this! You have a specific error message ("Stream is not readable") and the code that's causing it. 2. **Find Real-World Solutions with `ghx`:** We'll use `ghx`, a tool for searching GitHub, to find *actual projects* that have encountered (and solved!) this same issue. No more generic advice – you'll see working code. 3. **Consolidate and Analyze:** `ghx` brings those relevant code snippets into a single, easy-to-read file. 4. **Leverage Cursor's Composer:** We'll use Cursor's Composer to directly interact with the examples and your problematic code. Paste in your error, reference the `ghx`-generated file, and ask targeted questions like: * "Based on these examples, how should I modify my code to fix the 'stream is not readable' error?" * "What's the common cause of this error in the context of an MCP server?" * "Show me the specific lines in the examples that handle stream readability correctly." 5. **Fix the Error:** Implement the solution using this workflow. Instead of guessing, you'll learn directly from proven solutions. You'll gain a deeper understanding of your issue and how to troubleshoot it effectively. This approach transforms debugging from a frustrating hunt into a targeted learning experience.
2025-02-12 3 months ago / 未收藏/ egghead.io - Bite-sized Web Development Video Tutorials & Training发送到 kindle
Learn how to test custom ESLint rules with Vitest and RuleTester. In the [previous video](https://egghead.io/set-up-a-custom-es-lint-rule~kq49h), we created a custom eslint rule that gives us a warning when some of the class names don’t start with a lowercase letter. Here, I'll show you how to test this or any other custom ESLint rule using unit tests without manually restarting the ESLint server or modifying production code to check if the rules work correctly. We'll look at the structure in which correct and incorrect code samples should be specific to RuleTester. We'll also learn how to make our rule fixable and verify that the applied fix works as expected.
2025-02-12 3 months ago / 未收藏/ egghead.io - Bite-sized Web Development Video Tutorials & Training发送到 kindle
[https://github.com/johnlindquist/mcp-cursor-tool-starter/tree/main](https://github.com/johnlindquist/mcp-cursor-tool-starter/tree/main) Cursor's AI is powerful, but it can't do everything. What if you could give it the ability to interact with your specific workflows, tools, and data? This lesson shows you how to do exactly that by building a custom MCP tool – and we'll do it in under two minutes! We'll leverage the Model Context Protocol (MCP), an open standard that bridges the gap between AI agents (like Cursor's Composer) and your custom code. By creating an MCP tool, you're essentially giving Cursor a new skill. Here's the breakdown: Understanding MCP: We'll briefly cover what the Model Context Protocol (MCP) is and why it's the key to extending Cursor's functionality. In short, it enables communication between the AI and your tools in a standardized way. Fast Track Setup: We'll use a ready-made starter project (a single TypeScript file and package.json) to skip the boilerplate and get straight to the core concepts. Step-by-Step Integration: We'll walk through the simple process of: Cloning the starter project. Installing dependencies with pnpm install. Running the starter server. Adding the tool to Cursor's MCP settings (under Features > MCP). Verifying that your tool is working Real-World Example: We'll demonstrate the tool in action, using Cursor's Composer to create a new GitHub issue using the gh CLI, all driven by our custom MCP tool. This lesson is about more than just building one tool; it's about understanding how to extend Cursor's capabilities to fit your unique development needs. You'll learn the foundational steps to create a whole ecosystem of AI-powered tools.
2025-02-12 3 months ago / 未收藏/ egghead.io - Bite-sized Web Development Video Tutorials & Training发送到 kindle
Get started with MongoDB and Docker really quick 🔥 even if you have no backend/fullstack experience. This lesson guides you through installing Docker Desktop and setting up a MongoDB image. We explain Docker images and containers, and their roles in providing isolated software environments. We run a MongoDB image in 2 ways: using the terminal and Docker Desktop We demonstrates configuring port settings for a container and accessing the MongoDB shell (`mongosh`) to interact with the database. We learn how to manage Docker containers, configure settings, and run commands inside the container. Grab the CLI command: 🔥 `docker run --name some-mongo -d mongo:latest` 🔥
"阮一峰" / 2025-02-14 3 months ago / 未收藏/ 阮一峰的网络日志发送到 kindle
这里记录每周值得分享的科技内容,周五发布。
本杂志开源,欢迎投稿。另有《谁在招人》服务,发布程序员招聘信息。合作请邮件联系(yifeng.ruan@gmail.com)。

封面图


秦皇岛海边的某楼盘,像乐高积木一样,每层都有私人露台花园以及公共天台花园。(via

互联网创业几乎没了

上周我写了,AI 削弱互联网,网站行业前景黯淡。
发布后,我突然想到,如果这个判断正确,那么,互联网创业也差不多结束了

我说的"互联网创业",指的是那些纯粹线上、不涉及线下的创业项目。
大家有没有同样的感觉,互联网创业者正在急剧减少
我没有数字,但是自己的观察是,投身线上的开发者,一年比一年少。现在,除了 AI 和游戏领域,其他的互联网创业项目几乎没人敢碰。
回想十年前的氛围,简直天壤之别。那时,互联网创业堪称火爆,街头巷尾都在谈论。哪怕还没有一行代码,只要创业计划书写得好,就可能拿到风投。

那时的热门词汇是"互联网思维",彷佛只要沾上互联网,就有钱景,就可以火箭式增长。

时过境迁,十年前的盛况,如今烟消云散。到了今天,创业意愿低落,创业者少,鼓励你创业的人更少。更多的人劝你求稳,尽量选择体制内或者留在大公司。
就算你还是想创业,寻找风险投资也非常困难。投资者一看是互联网项目,就加倍警惕,一再追问现金流和利润有没有保证。
为什么互联网创业现在几乎消失了?
我看到一篇文章,总结了四点原因。
(1)互联网行业已经成熟了,留给创业者的机会大幅减少。互联网的大部分果实已被摘取,早期的高增长难以再现。真正的创新机会即使还能找到,也会被现有的大公司快速抄袭,不会留给创业者。
而且,AI 大模型出现后,互联网本身都在衰弱,它的创业机会就更少了。
(2)创业的机会成本变大了。一个大厂的高级工程师,现在的薪酬(包括股票期权)超过百万,创业很难打动他了。
(3)风险投资的商业模式难以实现了。风投的模式是,项目高速增长,最终实现上市退出,这越来越难做到了,能够指数式增长的线上项目现在基本找不到。
(4)创业者的生活态度发生了变化。人们比以前更重视生活质量,越来越不愿意接受创业带来的没日没夜的劳作、倦怠、失败的人际关系、心理健康问题。
上面的四条,第一条是根本原因:互联网的高增长结束,行业的机会少了。
Hacker News 社区的一位网友说得好:"浏览器技术已经到头了,通过 HTML 和 JS 在网页上组织信息、创造娱乐,并从中获利,很难翻出新花样了,你能创新的地方非常有限。"
总之,单纯的互联网创业,应该再也不会像以前那样兴旺了,很可能就是社会的平均增长率和回报率。未来互联网的机会更多是与其他行业结合,就好像现在的 AI 创业,很多都是 AI 为主,互联网为辅。

火山引擎 DeepSeek API 介绍

DeepSeek 是现在最热门的模型,但是你不一定要使用官方 API,完全可以用第三方 API 替代。
因为 DeepSeek 是开源模型,任何人都可以架设,第三方 API 其实跟官方的效果完全一样。
我用的就是第三方 DeepSeek API,服务商是火山引擎,今天就来说说怎么用。

火山引擎是字节旗下的云服务部门,实力和可靠性都有保证。除了自家的豆包大模型,它也提供其他大模型。
相比官方 API,它有一些显著的优点。
(1)免费额度高,50万的免费 token 额度,用完才收费。
(2)成本低。现在是五折优惠,R1 模型的百万 token 的输入价格为2元人民币,输出8元,比 DeepSeek 官方价格都要低。
(3)流量大。每分钟 token 限额(TPM)是500万,每日 token 限额(TPD)是50亿,都是全网最高,不用担心超过限额。
(4)延迟低。它在国内有多个机房,不管哪里连接,响应时间都在几十毫秒、甚至十几毫秒。
(5)联网搜索能力。它允许 DeepSeek 模型联网搜索,并且还允许用户定制联网能力(内容源、引用条数等)。
下面就是它的 DeepSeek API 的接入教程,很简单。
首先,登录它的大模型开发平台"火山方舟",选择左侧菜单的"在线推理",然后点击"创建推理接入点"(下图)。

接着,填写接口名称和选择模型,建议选择"DeepSeek-V3"(下图)。

接口开通成功后,系统会分配一个模型名称(比如,下图的 ep-20250213185631- 6b6r2),这个名称要记下。

下一步,就是客户端接入 DeepSeek API 了。本周正好有一篇读者投稿笔记软件 Obsidian 如何接入 DeepSeek API》,大家可以参考它,进行客户端配置,我不重复了。

客户端配置的关键一步,就是上面的配置页。Model Name 是系统刚才分配给你的模型名字,Provider 选择 OpenAI Format,Base URL 填写https://ark.cn-beijing.volces.com/api/v3,API key 就是你在火山引擎模型详情的"API 调用"里面,让系统生成的 API 密钥。
客户端配置完成后,就可以开始使用 DeepSeek API 了。

科技动态

1、人类的最后考试(Humanity's Last Exam)
今年1月份,两家美国 AI 公司推出了一个测试集,包含3000道各种学科的题目。

据他们说,只要 AI 模型通过了这个测试集,就表明 AI 智力已经超过了人类,也就是达到了 AGI(通用人工智能)的水平,所以起名为"人类的最后考试"。
截止2月3日,AI 模型取得的最佳成绩是26.6%的正确率。
按照专家的说法,AI 超过人类的智力,似乎是板上钉钉的事情了,唯一的悬念是何时能超过。这个"人类的最后考试"给出了衡量的具体方法。
2、VR 旋转椅
一家英国创业公司,推出了 VR 旋转椅,让玩家可以在 VR 世界里面,随着画面一起转身。

它在 VR 头盔的上方加了一个传感器(上图)。传感器能感知头部运动,从而发出指令给椅子。
当你坐在椅子上转动头部,椅子就会跟着你的头自动旋转。如果你的头向左旋转45°,椅子也会向左旋转45°。

它可以逼真地还原 VR 虚拟世界的转身体验,还消除了因为视野旋转而导致的眩晕感,提高了大脑对 VR 旋转的耐受度。

据发明者说,它还能提高生产力。如果你面前有一组虚拟显示器,只需转动脖子,你的整个身体就会从一个显示器切换到另一个显示器,虚拟键盘总是正对着你。
3、水下住宅
一家英国公司正在建造"水下住宅",将试验让人类在80米的水下生活。


上面是他们的设计图,下面是实际建造中的照片。


建造完成后,住客通过潜水艇,进入和离开这所水底房屋。

这看上去,只是一个有钱人的旅游项目,距离真正的"水下居民点"相差甚远。
事实上,人类移民水底的意义不大,不如建造海面上的"浮动城市",更有现实价值。
4、陶瓷砖熔炉
炼钢需要高温,融化铁矿石。炼钢的熔炉都烧煤炭,产生环境污染和排放二氧化碳。
钢铁厂无法改用电加热炼钢,因为普通的电加热达不到炼钢的温度,需要特殊材料的电热丝,那是非常贵的。

一家美国创业公司发明了不用煤炭的熔炉(上图),使用陶瓷砖来产生高温。
陶瓷砖(下图)有一个特点,可以不断升温,并能保持热量。只要用电不断加热,就能最终达到1,800摄氏度,融化铁矿石。

这个发明有助于钢铁厂告别煤炭,利用太阳能和风能产生的电力,让钢铁业变成绿色行业。

文章

1、如何用 OPNsense 搭建家庭防火墙(英文)

OPNsense 是一个开源的防火墙和路由软件,本文介绍自己安装的详细步骤。
2、《软件设计哲学》笔记(英文)

作者阅读了《软件设计哲学》这本书,对怎样减少软件的复杂性,做了详细的笔记,给出了代码示例。
3、CSS 的 backdrop-filter 属性(英文)

本文介绍 backdrop-filter 属性,可以产生毛玻璃的效果。
4、为什么你应该用 Canva 制作幻灯片(英文)

本文提出 Canva 是比 PowerPoint 更好的幻灯片制作工具。
5、在虚拟机中开发(英文)

作者介绍在 MacBook Pro 安装 Ubuntu 虚拟机,所有开发都在虚拟机里面完成,保证本机系统始终是干净的。
6、基于 signal 的 Web 组件(英文)

作者介绍自己写的一个 Web 组件,可以在不加其他 JS 库的情况下,实现 signal 功能。

工具

1、You-Get

一个从影音网站下载视频的命令行工具,比 yt-dlp 简单一点。
2、Zettlr

一个桌面的写作软件,可以管理文章,将其做成一本可出版的书。
3、Bruno

一个开源的 API 调试的桌面客户端,类似于 Postman。
4、Windows 容器

一个 Docker 镜像文件,在 Docker 容器里面运行 Windows。
5、Timeshift

Linux 的时光机器,定期对文件系统生成增量快照,可以返回到指定时点。
6、Pages CMS

一个静态网站的内容管理系统,在 Cloudflare Pages 托管你的网站,文章直接在网站上编辑,数据存放在 GitHub 仓库。
7、Pragmatic drag and drop

Atlassian 公司新发布的一款网页元素的拖拽库,Trello、Jira、Confluence 都在使用它。
8、Press UI

基于 uni-app 的小程序组件库。(@novlan1 投稿)
9、鸿蒙 ArkTS VSCode 插件
ArkTS 是华为鸿蒙系统的开发语言,属于 TypeScript 的超集,这是它的 VSCode 插件。(@Groupguanfang 投稿)
10、AutoSwitchTranslate
一个开源的 Chrome 插件,根据用户输入的语言,自动在谷歌翻译的页面上,切换中译英或英译中。(@wa008 投稿)

AI 相关

1、RAG Web UI

一个开源的 AI 桌面应用,可以上传文档,生成本地的知识库问答系统,基于 RAG(检索增强生成)技术。(@JohannLai 投稿)
2、TEN Agent

一个 AI 的工具框架,快速打造语音相关的 AI 应用。(@SyunWong 投稿)
3、We0

开源的 AI 代码生成方案,对标Cursor。(@we0-dev 投稿)
4、Gemini Pro Chatbot

一个开源的谷歌 Gemini Pro 手机客户端,基于 React Native。(@bravekingzhang 投稿)
5、Ncurator(馆长)

一个浏览器插件,通过导入文件或者爬取网页,建立自己的知识库,与内容聊天。(@Yoan98 投稿)
6、LLMs-Zero-to-Hero,完全从零手写大模型(视频)

网友投稿的一个视频,用一小时讲解从数据处理到模型训练,理解算法原理。(@bbruceyuan 投稿)
7、FreeParser

结合 OCR + LLM 的文档信息免费提取工具,适合处理发票、收据、简历等。(@hr98w 投稿)
8、AI 头像生成器

免费的头像合成网站。(@lyqtzs 投稿)

资源

1、WikiTok

网友用抖音风格制作的维基百科,每次划动出现一个随机页面。(@jianpingliu 投稿)
2、富文本编辑器比较2025版(英文)

这个页面详细比较了 JS 的富文本"所见即所得"编辑器,一共十几个库,详细介绍每个库的特点。
3、C 程序的可移植性

这篇长文用简单通俗的语言,总结了 C 程序移植到其他系统时需要注意的各种问题。

图片

1、小猫台灯
网友制作了一个小猫台灯,可以 3D 打印,提供源文件下载。

猫眼睛就是灯珠,猫的伊丽莎白圈是灯罩,放在屋里很有趣。

2、1920 年前的滑板车
许多人可能认为滑板车是最近才发明的,但实际上它们早在1915年就出现了。

当时,滑板车是一种经济实惠且高效的交通方式,比汽车和摩托车更便宜和省油。


文摘

1、中国的高科技产业集群
近年来,中国企业在电动汽车、手机和无人机等消费产品领域变得极具竞争力。与此同时,它们在各种高价值零部件和机械产品领域也变得极具竞争力,例如计算机芯片、机器人、激光雷达和电池。
中国企业是如何突然在所有这些领域都表现出色的?西方的一种常见的解释是,政府给予这些行业大量补贴,但一个叫做凯尔·陈(Kyle Chan)的学者有不同的解释。
他的观点是,上面这些产品属于相关技术的单一集群。
首先,很多东西都有助于生产其他东西。电池用于电动汽车、手机和无人机,芯片也是如此,工业机器人有助于制造所有其他东西,诸如此类。
如果你把所有上游产业都放在同一个国家----或者,如果可能的话,放在同一个城市----那么你就可以很容易地同时在所有下游产业中具有竞争力。这使得大国比小国更具优势----拥有更大的国内市场,更容易支持更多种类的上游产业。这对产业政策也非常重要----它告诉我们,建立一个完整的本地产业生态系统可以产生积极的外部效应。
其次,很多技术似乎正在融合。汽车与手机之间的区别已经比以前小了很多,基本上,电动汽车和手机都是由金属和塑料包裹着电池和一些计算机芯片。无人机就是这些东西加上一个发动机。
这意味着,如果一家公司擅长制造其中一种产品,那么它很容易开始制造其他产品。这就是小米能够如此迅速地建立电动汽车业务的原因。这也意味着,如果一家公司生产所有下游产品,那么它很容易扩展到上游行业----就像比亚迪成为芯片制造商一样。
不管怎样,Kyle Chan 关注的是中国的优势,而不是美国的劣势。但很容易看出,美国在这个新兴技术集群中的竞争将面临很多麻烦。美国的保守派领导人狂热地反对电动汽车和电池,而工会普遍反对自动化。这将使美国的工业生态系统出现巨大漏洞,最终损害半导体、手机和无人机行业。
但同时,我认为 Kyle Chan 描述的现象最终可能会给中国企业带来挑战。他指出,中国大公司越来越多地生产完全相同的产品。这种缺乏差异化将导致恶性价格竞争,从而导致利润率低下。
上世纪80年代泡沫时期,日本大型制造公司也发生了类似的事情----松下、索尼、日立、东芝、夏普、JVC、三洋等公司基本上都生产同一种电子产品、家电、零部件和机械。由于它们在每个产品类别中都展开竞争,因此利润率一直很低。同样,我们可能会看到比亚迪、小米、华为和其他一批中国大公司相互竞争,争夺利润。

言论

1、
程序员们不再互相提问,AI 回答了大部分问题。
-- 《AI 的数周相当于人类的几十年》,自从 AI 大模型问世后,问答网站 StackOverflow 日益冷清
2、
有一句老话:创意很廉价,执行才是一切。然而,AI 颠覆了这个说法,执行现在很廉价,整个开发时间和交付速度的概念都不同了。
未来属于那些有想法、还能动手去做的人。
-- ghuntley.com
3、
有时候,与那些固执己见、不肯改变观点的人,进行辩论是值得的。也许他永远不会让步,但你可以帮助其他人,看清他的胡说八道。
当然,你要警惕,不要给不法之徒提供表演的舞台,而且你的时间和精力是有限的。
-- Reddit 读者
4、
创办一家公司并不真的需要一个商业计划,而只需要前进。
-- 马云,第一次与蔡崇信见面时说的话

往年回顾

苹果头盔的最大问题(#290)
教育年限可以缩短吗?(#240)
产品化思维(#190)
印度人的工资是多少?(#140)
(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
  • 发表日期: 2025年2月14日
"Anthony Grutta" / 2025-02-12 3 months ago / 未收藏/ Todd Motto发送到 kindle
Discover how to use GitHub Copilot to refactor your code and see samples of it in action.
The post How to refactor code with GitHub Copilot appeared first on The GitHub Blog.
"Brandon Stewart" / 2025-02-13 3 months ago / 未收藏/ Todd Motto发送到 kindle
How GitHub’s Product Security Engineering team manages our CodeQL implementation at scale and how you can, too.
The post How GitHub uses CodeQL to secure GitHub appeared first on The GitHub Blog.
"Jakub Oleksy" / 2025-02-13 3 months ago / 未收藏/ Todd Motto发送到 kindle
In January, we experienced two incidents that resulted in degraded performance across GitHub services.
The post GitHub Availability Report: January 2025 appeared first on The GitHub Blog.
"Abel Boros" / 2025-02-13 3 months ago / 未收藏/ RisingStack Engineering - Node.js Tutorials & Resources发送到 kindle
In Hungary, the challenge of predicting solar power generation accurately is critical as the country taps into its photovoltaic potential of 1750 PJ per year. With solar power already making up 25% of the total grid demand, reliable short-term forecasts are needed to manage the variability in energy production. Our project developed an AI-based prediction […]
The post Accurate Solar Power Prediction: A Real-Time Nowcasting Solution appeared first on RisingStack Engineering.
"Daniel Rosenwasser" / 2025-02-14 3 months ago / 未收藏/ TypeScript发送到 kindle
Today we are excited to announce the Release Candidate (RC) of TypeScript 5.8! To get started using the Release Candidate, you can get it through npm with the following command: npm install -D typescript@rc Let’s take a look at what’s new in TypeScript 5.8! What’s New Since the Beta? Since our beta release, we have […]
The post Announcing TypeScript 5.8 RC appeared first on TypeScript.
"Pankaj" / 2025-02-14 3 months ago / 未收藏/ DigitalOcean Community Tutorials发送到 kindle

Introduction

Java Genrics is one of the most important features introduced in Java 5. If you have been working on Java Collections and with version 5 or higher, I am sure that you have used it. Generics in Java with collection classes is very easy but provides many more features than just creating the type of collection. We will try to learn the features of generics in this article.

Generics in Java

Generics was added in Java 5 to provide compile-time type checking and removing risk of ClassCastException that was common while working with collection classes. The whole collection framework was re-written to use generics for type-safety. Let’s see how generics help us using collection classes safely.
List list = new ArrayList();
list.add("abc");
list.add(new Integer(5)); //OK

for(Object obj : list){
 //type casting leading to ClassCastException at runtime
    String str=(String) obj; 
}
Above code compiles fine but throws ClassCastException at runtime because we are trying to cast Object in the list to String whereas one of the element is of type Integer. After Java 5, we use collection classes like below.
List<String> list1 = new ArrayList<String>(); // java 7 ? List<String> list1 = new ArrayList<>(); 
list1.add("abc");
//list1.add(new Integer(5)); //compiler error

for(String str : list1){
     //no type casting needed, avoids ClassCastException
}
Notice that at the time of list creation, we have specified that the type of elements in the list will be String. So if we try to add any other type of object in the list, the program will throw compile-time error. Also notice that in for loop, we don’t need typecasting of the element in the list, hence removing the ClassCastException at runtime.

Java Generic Class

We can define our own classes with generics type. A generic type is a class or interface that is parameterized over types. We use angle brackets (<>) to specify the type parameter. To understand the benefit, let’s say we have a simple class as:
package com.journaldev.generics;

public class GenericsTypeOld {

 private Object t;

 public Object get() {
  return t;
 }

 public void set(Object t) {
  this.t = t;
 }

        public static void main(String args[]){
  GenericsTypeOld type = new GenericsTypeOld();
  type.set("Pankaj"); 
  String str = (String) type.get(); //type casting, error prone and can cause ClassCastException
 }
}
Notice that while using this class, we have to use type casting and it can produce ClassCastException at runtime. Now we will use java generic class to rewrite the same class as shown below.
package com.journaldev.generics;

public class GenericsType<T> {

 private T t;
 
 public T get(){
  return this.t;
 }
 
 public void set(T t1){
  this.t=t1;
 }
 
 public static void main(String args[]){
  GenericsType<String> type = new GenericsType<>();
  type.set("Pankaj"); //valid
  
  GenericsType type1 = new GenericsType(); //raw type
  type1.set("Pankaj"); //valid
  type1.set(10); //valid and autoboxing support
 }
}
Notice the use of GenericsType class in the main method. We don’t need to do type-casting, and we can remove ClassCastException error at runtime. If we don’t provide the type at the creation time, the compiler will warn that “GenericsType is a raw type. References to generic type GenericsType<T> should be parameterized”. When we don’t provide the type, the type becomes Object, and hence, it allows both String and Integer objects. But, we should always try to avoid this because we will have to use type casting while working on the raw type, which can produce runtime errors.
Tip: We can use @SuppressWarnings("rawtypes") annotation to suppress the compiler warning, check out java annotations tutorial.
Also, please note that it supports java autoboxing.

Java Generic Interface

Comparable interface is a great example of Generics in interfaces and it’s written as:
package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}
In similar way, we can create generic interfaces in java. We can also have multiple type parameters as in Map interface. Again we can provide parameterized value to a parameterized type also, for example new HashMap<String, List<String>>(); is valid.

Java Generic Type

Java Generic Type Naming convention helps us understanding code easily and having a naming convention is one of the best practices of Java programming language. So generics also comes with its own naming conventions. Usually, type parameter names are single, uppercase letters to make it easily distinguishable from java variables. The most commonly used type parameter names are:
  • E - Element (used extensively by the Java Collections Framework, for example ArrayList, Set etc.)
  • K - Key (Used in Map)
  • N - Number
  • T - Type
  • V - Value (Used in Map)
  • S,U,V etc. - 2nd, 3rd, 4th types

Java Generic Method

Sometimes we don’t want the whole class to be parameterized, in that case, we can create java generics method. Since the constructor is a special kind of method, we can use generics type in constructors too. Here is a class showing an example of a java generic method.
package com.journaldev.generics;

public class GenericsMethods {

 //Java Generic Method
 public static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){
  return g1.get().equals(g2.get());
 }
 
 public static void main(String args[]){
  GenericsType<String> g1 = new GenericsType<>();
  g1.set("Pankaj");
  
  GenericsType<String> g2 = new GenericsType<>();
  g2.set("Pankaj");
  
  boolean isEqual = GenericsMethods.<String>isEqual(g1, g2);
  //above statement can be written simply as
  isEqual = GenericsMethods.isEqual(g1, g2);
  //This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.
  //Compiler will infer the type that is needed
 }
}
Notice the isEqual method signature showing syntax to use generics type in methods. Also, notice how to use these methods in our java program. We can specify type while calling these methods or we can invoke them like a normal method. Java compiler is smart enough to determine the type of variable to be used, this facility is called type inference.

Java Generics Bounded Type Parameters

Suppose we want to restrict the type of objects that can be used in the parameterized type, for example in a method that compares two objects and we want to make sure that the accepted objects are Comparables. To declare a bounded type parameter, list the type parameter’s name, followed by the extends keyword, followed by its upper bound, similar like below method.
public static <T extends Comparable<T>> int compare(T t1, T t2){
  return t1.compareTo(t2);
 }
The invocation of these methods is similar to unbounded method except that if we will try to use any class that is not Comparable, it will throw compile-time error. Bounded type parameters can be used with methods as well as classes and interfaces. Java Generics supports multiple bounds also, i.e <T extends A & B & C>. In this case, A can be an interface or class. If A is class then B and C should be an interface. We can’t have more than one class in multiple bounds.

Java Generics and Inheritance

We know that Java inheritance allows us to assign a variable A to another variable B if A is subclass of B. So we might think that any generic type of A can be assigned to generic type of B, but it’s not the case. Let’s see this with a simple program.
package com.journaldev.generics;

public class GenericsInheritance {

 public static void main(String[] args) {
  String str = "abc";
  Object obj = new Object();
  obj=str; // works because String is-a Object, inheritance in java
  
  MyClass<String> myClass1 = new MyClass<String>();
  MyClass<Object> myClass2 = new MyClass<Object>();
  //myClass2=myClass1; // compilation error since MyClass<String> is not a MyClass<Object>
  obj = myClass1; // MyClass<T> parent is Object
 }
 
 public static class MyClass<T>{}

}
We are not allowed to assign MyClass<String> variable to MyClass<Object> variable because they are not related, in fact MyClass<T> parent is Object.

Java Generic Classes and Subtyping

We can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses. For example, ArrayList<E> implements List<E> that extends Collection<E>, so ArrayList<String> is a subtype of List<String> and List<String> is subtype of Collection<String>. The subtyping relationship is preserved as long as we don’t change the type argument, below shows an example of multiple type parameters.
interface MyList<E,T> extends List<E>{
}
The subtypes of List<String> can be MyList<String,Object>,MyList<String,Integer> and so on.

Java Generics Wildcards

Question mark (?) is the wildcard in generics and represent an unknown type. The wildcard can be used as the type of a parameter, field, or local variable and sometimes as a return type. We can’t use wildcards while invoking a generic method or instantiating a generic class. In the following sections, we will learn about upper bounded wildcards, lower bounded wildcards, and wildcard capture.

Java Generics Upper Bounded Wildcard

Upper bounded wildcards are used to relax the restriction on the type of variable in a method. Suppose we want to write a method that will return the sum of numbers in the list, so our implementation will be something like this.
public static double sum(List<Number> list){
  double sum = 0;
  for(Number n : list){
   sum += n.doubleValue();
  }
  return sum;
 }
Now the problem with above implementation is that it won’t work with List of Integers or Doubles because we know that List<Integer> and List<Double> are not related, this is when an upper bounded wildcard is helpful. We use generics wildcard with extends keyword and the upper bound class or interface that will allow us to pass argument of upper bound or it’s subclasses types. The above implementation can be modified like the below program.
package com.journaldev.generics;

import java.util.ArrayList;
import java.util.List;

public class GenericsWildcards {

 public static void main(String[] args) {
  List<Integer> ints = new ArrayList<>();
  ints.add(3); ints.add(5); ints.add(10);
  double sum = sum(ints);
  System.out.println("Sum of ints="+sum);
 }

 public static double sum(List<? extends Number> list){
  double sum = 0;
  for(Number n : list){
   sum += n.doubleValue();
  }
  return sum;
 }
}
It’s similar like writing our code in terms of interface, in the above method we can use all the methods of upper bound class Number. Note that with upper bounded list, we are not allowed to add any object to the list except null. If we will try to add an element to the list inside the sum method, the program won’t compile.

Java Generics Unbounded Wildcard

Sometimes we have a situation where we want our generic method to be working with all types, in this case, an unbounded wildcard can be used. Its same as using <? extends Object>.
public static void printData(List<?> list){
  for(Object obj : list){
   System.out.print(obj + "::");
  }
 }
We can provide List<String> or List<Integer> or any other type of Object list argument to the printData method. Similar to upper bound list, we are not allowed to add anything to the list.

Java Generics Lower bounded Wildcard

Suppose we want to add Integers to a list of integers in a method, we can keep the argument type as List<Integer> but it will be tied up with Integers whereas List<Number> and List<Object> can also hold integers, so we can use a lower bound wildcard to achieve this. We use generics wildcard (?) with super keyword and lower bound class to achieve this. We can pass lower bound or any supertype of lower bound as an argument, in this case, java compiler allows to add lower bound object types to the list.
public static void addIntegers(List<? super Integer> list){
  list.add(new Integer(50));
 }

Subtyping using Generics Wildcard

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. List<? extends Integer> is a subtype of List<? extends Number>

Java Generics Type Erasure

Generics in Java was added to provide type-checking at compile time and it has no use at run time, so java compiler uses type erasure feature to remove all the generics type checking code in byte code and insert type-casting if necessary. Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead. For example, if we have a generic class like below;
public class Test<T extends Comparable<T>> {

    private T data;
    private Test<T> next;

    public Test(T d, Test<T> n) {
        this.data = d;
        this.next = n;
    }

    public T getData() { return this.data; }
}
The Java compiler replaces the bounded type parameter T with the first bound interface, Comparable, as below code:
public class Test {

    private Comparable data;
    private Test next;

    public Node(Comparable d, Test n) {
        this.data = d;
        this.next = n;
    }

    public Comparable getData() { return data; }
}

Common Mistakes with Generics

Even experienced developers make mistakes while using generics. Here are some common pitfalls:

1. Using Raw Types

Using raw types defeats the purpose of generics.
List list = new ArrayList(); // Avoid this
list.add("Hello");
list.add(123); // No type safety
Instead,use parameterized types instead:
List<String> list = new ArrayList<>();

2. Mixing Generics with Legacy Code

Avoid mixing generic and non-generic code as it can lead to unexpected issues.

3. Incorrect Use of Wildcards

Example:
public void addItem(List<? extends Number> list) {
    // list.add(10); // Compilation error
}
Instead, use ? super T if modification is required:
public void addItem(List<? super Integer> list) {
    list.add(10);
}

4. Overusing Wildcards

While wildcards improve flexibility, using them unnecessarily can make the code harder to understand.

FAQs

1. What is Generics in Java?

Generics allow you to define a class or method with a type parameter. For example:
class Box<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}
Here, T is a type parameter that can be replaced with any concrete type at runtime.

2. Why Use Generics in Java?

  • Type Safety: Prevents ClassCastException at runtime.
  • Code Reusability: Enables writing a single class or method that works with different types.
  • Improved Readability: Avoids excessive casting and makes code cleaner.
For a deeper understanding of Java fundamentals, check out the Java EE tutorial.

3. Can you give an example of a generic method?

A generic method can take a type parameter:
public static <T> void printArray(T[] elements) {
    for (T element : elements) {
        System.out.println(element);
    }
}
This method prints elements of an array of any type.

4. What Does List<?> in Java Mean?

The ? is a wildcard in Java generics. It represents an unknown type. For example:
List<?> list = new ArrayList<String>();
This means the list can hold any type, but its exact type is unknown at compile time. You can learn more about comparators in java in this tutorial on Comparable and Comparator in Java Example.

5. Understanding Wildcards in Java Generics

There are two main types of wildcards:
  1. ? extends T: Represents an unknown type that is a subclass of T.
       public void processElements(List<? extends Number> numbers) {
     for (Number num : numbers) {
         System.out.println(num);
       }
    }
    
  2. ? super T: Represents an unknown type that is a superclass of T.
    public void addNumbers(List<? super Integer> numbers) {
     numbers.add(42);
    }
    
Wildcards provide flexibility while maintaining type safety.

6. Can I Create an Array of Generics in Java?

No, Java does not allow generic array creation due to type erasure. This would lead to ClassCastException error. Instead, you can use a List<T>:
List<String> list = new ArrayList<>();
For more details on Java type annotations, refer to our tutorial on Java Annotations.

7. Why Are Generics Safe in Java?

Generics enforce type checking at compile time, reducing the likelihood of runtime errors. They eliminate the need for explicit casting and make code more readable and maintainable.

8. What Are the Rules for Generics in Java?

  1. Cannot create instances of a generic type.
    class Box<T> {
     // T obj = new T(); // Illegal
    }
    
  2. No static members of generic type.
    class Box<T> {
       // static T instance; // Illegal
    }
    
  3. Cannot use primitives as type parameters (use wrapper classes instead).
    // List<int> list = new ArrayList<>(); // Illegal
    List<Integer> list = new ArrayList<>();
    

Conclusion

Java Generics offers a robust mechanism to ensure type safety and promote code reusability. By grasping the nuances of wildcards, avoiding common pitfalls, and adhering to best practices, you can craft more maintainable, efficient, and error-free Java applications.
For more Java-related topics, you can refer to our tutorials on Java EE Tutorials and Comparable and Comparator examples.
"Adrien Payong" / 2025-02-14 3 months ago / 未收藏/ DigitalOcean Community Tutorials发送到 kindle

Introduction

GPU acceleration has revolutionized deep learning, scientific computing, and machine learning, enhancing performance over traditional CPU computations.
This guide shows you how to install CUDA and cuDNN for GPU, enabling tasks like neural network training, large-scale data analysis, and complex simulations.
We’ll discuss compatibility considerations, troubleshooting advice, and best practices for ensuring a smooth GPU setup for CUDA.
By adhering to this guide, you’ll use the full capabilities of GPU for faster and more efficient computational processes.

Prerequisites

Having a solid grounding in some concepts will improve your understanding of this guide:
  • Basic Computer Proficiency: Ability to navigate your OS (whether it’s Windows or Linux) and complete basic tasks related to file management.
  • Familiarity with Command-Line Tools
  • Understanding of GPUs: A general understanding of GPUs and their advantages over CPUs, particularly regarding parallel processing and applications in machine learning.
  • Basic Understanding of Machine Learning/Deep Learning: Familiarity with popular frameworks such as TensorFlow or PyTorch and their utilization of GPUs to speed model training.
  • Programming Fundamentals: Some experience with languages like Python since the guide incorporates code snippets to verify installation and framework configuration.
  • Understanding of System Architecture: Awareness of whether your system is 64-bit, along with understanding the difference between drivers, libraries, and software dependencies.
  • Understanding of Environment Variables: A basic understanding of how to set environment variables (for instance, PATH and LD_LIBRARY_PATH) is important to configure the software.

What are CUDA and cuDNN

CUDA (Compute Unified Device Architecture) is a groundbreaking platform for parallel computing created by NVIDIA. It provides programmers and researchers direct access to NVIDIA GPUs’ virtual instruction set. CUDA improves the efficiency of complex operations such as training AI models, processing large datasets, and conducting scientific simulations.
cuDNN (CUDA Deep Neural Network library) is a specialized, GPU-accelerated library that provides essential building blocks for deep neural networks. It’s designed to deliver high-performance components for convolutional neural networks, recurrent neural networks (RNNs), and other complex deep learning algorithms. By implementing cuDNN, frameworks such as TensorFlow and PyTorch can take advantage of optimized GPU performance.
In short, NVIDIA’s CUDA installation lays the groundwork for GPU computing, whereas cuDNN provides targeted resources for deep learning. This combination enables remarkable GPU acceleration for tasks that a traditional CPU could otherwise require days or weeks to complete.

System Requirements and Preparations

Before you start the NVIDIA CUDA installation or cuDNN installation steps, please ensure your system fulfills the following requirements:
  • CUDA-Enabled NVIDIA GPU: Verify if your GPU is included in NVIDIA’s list of CUDA-enabled GPUs. While most recent NVIDIA GPUs support CUDA, it’s wise to check.
  • If using Linux, launch a terminal and execute lspci | grep—i nvidia to identify your GPU. Then, check its CUDA compatibility on NVIDIA’s official site.
  • Sufficient Disk Space: Setting up CUDA, cuDNN, and the necessary drivers may require several gigabytes of storage. You must have a minimum of 5–10 GB of free disk space available.
  • Administrative Privileges: Installation on Windows and Ubuntu requires admin or sudo rights.
  • NVIDIA GPU Drivers: You need to install the latest drivers on your machine. While this can often be included in the CUDA installation process, it is advisable to verify that you have the latest drivers directly from NVIDIA’s website.
To get deeper into the GPU capabilities, explore our article related to Nvidia CUDA with H100.

Installing CUDA and cuDNN on Windows

This section provides a detailed guide on installing CUDA and cuDNN on a Windows system.
Install CUDA and cuDNN on Windows

Step 1: Verify GPU Compatibility

To determine your GPU model and check if it is compatible with CUDA, right-click on the Start Menu, choose Device Manager, and then expand the Display Adapters section to locate your NVIDIA GPU. After finding it, head over to the NVIDIA CUDA-Enabled GPU List to verify whether the specific GPU model supports CUDA for GPU acceleration.

Step 2: Install NVIDIA GPU Drivers

To download and set up the latest NVIDIA drivers, go to the NVIDIA Driver Downloads section and choose the correct driver for your GPU and Windows version. Then, execute the downloaded installer and follow the instructions on your screen. After you’ve installed the driver, make sure to restart your system to apply the changes.

Step 3: Install the CUDA Toolkit

To start, go to the CUDA Toolkit Archive and select the version that aligns with your project needs. If you’re using guidelines like How to install CUDA and cuDNN on GPU 2021,” it might be wise to choose a version from that timeframe to maintain compatibility with previous frameworks.
You will choose your operating system, such as Windows, with the architecture, typically x86_64. You will also indicate your Windows version, whether Windows 10 or 11.
After selection, you can download either the local .exe installer or the network installer. Next, execute the downloaded installer and proceed through the installation prompts. During this process, ensure you choose all essential components, such as the CUDA Toolkit, sample projects, and documentation, to set up a comprehensive development environment.
The installer will copy the necessary files to the default directory: C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\vX.X. In this case, X.X represents the specific version of CUDA you are installing.
Finally, while the installer generally manages environment variables automatically, it’s important to check them. Open the command prompt and execute the following commands to confirm that the CUDA\_PATH and PATH variables point to the correct CUDA directories:
*echo %CUDA\_PATH%*    
*echo %PATH%*

Step 4: Download and Install cuDNN on Windows

  • Register as an NVIDIA Developer: To gain access, you need to set up an account on the NVIDIA Developer website to access cuDNN downloads.
  • Check Compatibility: It’s important to ensure the cuDNN version aligns with your installed CUDA version. If you have, for example, CUDA 11.8, search specifically for cuDNN 8 builds that indicate they support CUDA 11.8.

Using the Installer

Download the cuDNN installer for Windows and run it, following the on-screen prompts. During installation, choose either Express or Custom installation based on your preference.

Manual Installation

Unzip the downloaded file for manual installation and place it in a temporary folder. Then copy:
bin\\cudnn\*.dll to C:\\Program Files\\NVIDIA\\CUDNN\\vx.x\\bin,
include\\cudnn\*.h to C:\\Program Files\\NVIDIA\\CUDNN\\vx.x\\include,
lib\\x64\\cudnn\*.lib to C:\\Program Files\\NVIDIA\\CUDNN\\vx.x\\lib.
Replace ‘x.x’ with your version number.
Lastly, update your system’s PATH variable by adding C:\\Program Files\\NVIDIA\\CUDNN\\vx.x\\bin to ensure you can access cuDNN executables properly.

Verification

Check the folder contents to verify that the cuDNN files are correctly placed. You should find a cudnn64_x.dll file in the bin directory and .h header files in the include directory.

Step 5: Environment Variables on Windows

Although the CUDA installer typically manages environment variables automatically, it is wise to verify that all configurations are accurate:
  1. Open System Properties
    • Right-click on This PC (or Computer) and choose Properties.
    • Go to Advanced System Settings, and then click on Environment Variables.
  2. Check CUDA_PATH
    • In the section labeled System variables, search for CUDA_PATH.
    • It should direct to: C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\vX.X. Replace X.X with the version of CUDA that is installed (e.g., v11.8).
  3. Path Variable
    In the same section, under System variables, find and select Path.
    Check that the following directory is included: C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\vX.X\\bin. You may also find: C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\vX.X\\libnvvp. If it’s not there, add it manually to help the system locate CUDA executables.
  4. Add cuDNN If Needed
    Generally, copying cuDNN files into the appropriate CUDA folders (bin, include, lib) is sufficient. If you keep cuDNN in a different location, add that folder path to your Path variable so Windows can find the cuDNN libraries.

Installing CUDA on Ubuntu

This section shows how to install the CUDA Toolkit on your Ubuntu system. It covers repository setup, GPG key verification, and package installation.
Install CUDA on Ubuntu

Step 1: Install Required Packages

Ensure that curl is installed on your system:
sudo apt update
sudo apt install curl

Step 2: Install NVIDIA Drivers

Before installing CUDA, it’s essential to have the appriproriate NVIDIA drivers intalled—to do so:
sudo ubuntu-drivers autoinstall
Then, make sure to reboot your system after driver installation
sudo reboot

Step 3: Add the NVIDIA GPG Key

To ensure the authenticity of packages from the NVIDIA repository, add the NVIDIA GPG key
curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/3bf863cc.pub | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-cuda-keyring.gpg
The curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/3bf863cc.pub command uses curl to retrieve the public key from the designated URL. The flags included are:
  • -f: Silently fail in the event of server errors.
  • -s: Operate in silent mode (no progress indicators or error notifications).
  • -S: Show error notifications when -s is used.
  • -L: Follow redirects.
The | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-cuda-keyring.gpg segment takes the output from the curl directive into gpg, which converts the key from ASCII to binary format and saves it in the chosen location. The sudo command guarantees you have the required permissions.
The resulting binary key is stored in /usr/share/keyrings/nvidia-cuda-keyring.gpg, allowing the Ubuntu system to verify the package integrity from NVIDIA’s CUDA repository.

Step 4: Add the CUDA Repository

Incorporate the CUDA repository that corresponds to your Ubuntu version. For example, for Ubuntu 22.04 you can run:
echo "deb [signed-by=/usr/share/keyrings/nvidia-cuda-keyring.gpg] https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/ /" | sudo tee /etc/apt/sources.list.d/cuda-repository.list
  • echo "deb \[signed-by=/usr/share/keyrings/nvidia-cuda-keyring.gpg\] https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86\_64/: This command sets up a line that indicates the repository URL and the keyring for package signing.
  • | *sudo tee /etc/apt/sources.list.d/cuda-repository.list: This command sends the output from echo to tee, which writes it into the specified file or creates it if it doesn’t exist. The sudo ensures you have permission to alter system files.
There are many repository tags for different Ubuntu versions and you can adjust the URL as required if you use a different Ubuntu version (e.g., ubuntu2004 or ubuntu1804).

Step 5: Update the Package Repository

Now, you can update your package list to include the new repository:
sudo apt update
This guarantees that Ubuntu can recognize and fetch packages from the NVIDIA CUDA repository.

Step 6: Install the CUDA Toolkit

Install the CUDA Toolkit with the following command:
sudo apt install cuda
This command will install all the CUDA components necessary for GPU acceleration, including compilers and libraries. It’s important to note that this will install the latest version of CUDA. If you’re looking for a particular version, you’ll need to specify it, such as sudo apt install cuda-11-814.

Step 7: Set Up Environment Variables

To ensure CUDA is available whenever you open a new terminal session, add these lines to your ~/.bashrc file:
export PATH=/usr/local/cuda/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
The first line places /usr/local/cuda/bin at the beginning of your PATH, making the nvcc compiler accessible.
The second line appends /usr/local/cuda/lib64 to your LD_LIBRARY_PATH, assisting the system in locating CUDA libraries. The specific paths will depend on the installed version of CUDA.
Note: The .bashrc file is a hidden shell script found within your home directory that is executed each time you initiate a new interactive terminal session within the Bash shell. It includes commands for setting up your environment, like environment variables, aliases, and functions, which customize and manage your shell behavior each time you launch a terminal.
Finally, reload your .bashrc so the new enviornment variables take effect right away:
source ~/.bashrc

Step 8: Verification

Verify that CUDA was installed successfully:
nvcc --version
If CUDA is correctly installed, this command will display the installed CUDA version.
By completing these steps, you’ve successfully installed CUDA on Ubuntu, set up the essential environment variables, and prepared your system for GPU-accelerated applications.

Installing cuDNN on Ubuntu

Thanks to NVIDIA’s package manager support, installing cuDNN on Linux has been simplified. Here is a brief guide that outlines both the recommended package manager method (for Ubuntu/Debian systems) and the manual installation process in case packages are unavailable for your specific distribution.
Note: If the package manager is available for your Linux distribution, it tends to be the easiest and most manageable option. Also, when performing a manual installation, pay careful attention to file paths, versions, and permissions to ensure that cuDNN works flawlessly with your existing CUDA configuration.

Step 1: Download cuDNN

  1. Go to the official NVIDIA cuDNN download page.
  2. Sign in to your NVIDIA Developer account (or create one if you don’t have it).
  3. Choose the cuDNN version that corresponds with your installed CUDA version.
  4. Download the Linux package (usually provided as a .tar.xz file) if you intend to install it manually, or take note of the version strings if you prefer to use your package manager.

Step 2: Install cuDNN

Option A: Using the Package Manager

For Ubuntu or Debian-based distributions, NVIDIA recommends installing cuDNN via apt:
sudo apt-get install libcudnn8=8.x.x.x-1+cudaX.X
sudo apt-get install libcudnn8-dev=8.x.x.x-1+cudaX.X
  • Swap 8.x.x.x with the actual cuDNN version you have downloaded.
  • Replace X.X to match your installed CUDA version (for example, cuda11.8).

Option B: Manual Installation

If the package manager is unavailable or not supported in your distribution, first extract the archive using this command:
tar -xf cudnn-linux-x86_64-x.x.x.x_cudaX.X-archive.tar.xz
Update x.x.x.x (cuDNN version) and X.X (CUDA version) to correspond with the versions stated in your archive’s name.
Then, copy the cuDNN files using the following command:
sudo cp cudnn-*-archive/include/cudnn*.h /usr/local/cuda/include/
sudo cp -P cudnn-*-archive/lib/libcudnn* /usr/local/cuda/lib64/
sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn*
This series of instructions copy the cuDNN header files (cudnn\*.h) to the CUDA include folder and copy the cuDNN library files (libcudnn\) to the CUDA library folder. By using the \-P option, any symbolic links will be maintained during this copy. chmod a+r grants read permissions to all users for these files, thereby ensuring they are accessible across the system.

Step 3: Update the Shared Library Cache

Regardless of whether you used the package manager for installation or manually copied the files, it’s important to refresh the library cache of your system:
sudo ldconfig
This step ensures that your operating system recognizes the newly added cuDNN libraries.

Step 4: Verify the Installation

To verify if cuDNN has been installed correctly, you can check the version details in cudnn.h:
cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2
This command will display the cuDNN version installed on your system by extracting specific lines from the cudnn.h header file. The component grep CUDNN_MAJOR -A 2 narrows the output to display the major version number alongside the subsequent two lines, usually indicating the minor and patch version numbers.
If the installed cuDNN version is 8.9.2, the executed command may yield:
#define CUDNN_MAJOR 8
#define CUDNN_MINOR 9
#define CUDNN_PATCHLEVEL 2

Step 5: Update Enviornment Variables

Finally, add the CUDA binary and library directories to your PATH and LD_LIBRARY_PATH so your system can locate cuDNN and CUDA files.
First, edit (or create) the ~/.bashrc file:
export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
Then, apply the changes in the current shell session:
source ~/.bashrc

Version Compatibility and Framework Integration

Various deep learning frameworks require specific CUDA and cuDNN versions. Below is a general guideline:
Framework Supported CUDA Versions Supported cuDNN Versions Notes
Tensorflow 11.2 - 12.2 8.1+ TensorFlow 2.15 is compatible with CUDA 12.2. Prior versions may require specific CUDA versions.
PyTorch 11.3 - 12.1 8.3.2+ PyTorch 2.1 is compatible with CUDA versions 11.8 and 12.1. The exact versions will vary based on the PyTorch release.
MXNet 10.1 - 11.7 7.6.5 - 8.5.0 MXNet 1.9.1 supports up to CUDA 11.7 and cuDNN 8.5.0.
Caffee 10.0 - 11.x 7.6.5 - 8.x Caffe typically requires manual compilation. It is advisable to verify specific version requirements.
It is essential to consistently refer to the official documentation for each framework, as compatibility may change with subsequent releases.

Additional Notes

  • The latest version of TensorFlow (version 2.16.1) has simplified the installation of the CUDA library on Linux with pip.
  • PyTorch binaries come pre-packaged with specific versions of CUDA and cuDNN…
  • MXNet requires accurate matching of the CUDA and cuDNN versions.
  • Installing JAX with CUDA and cuDNN support can be complex and often demands specific combinations of versions.
Modern deep learning tools work great with CUDA and cuDNN, providing significant speed improvements on systems equipped with GPUs. Here’s a quick rundown on setting up TensorFlow, PyTorch, and other popular libraries to get the most out of GPU acceleration.

Tensorflow GPU Setup

Install TensorFlow with GPU Support

pip install tensorflow[and-cuda]
This command installs TensorFlow along with necessary CUDA dependencies. For Windows users, GPU support is generally enabled through WSL2(Windows Subsystem for Linux 2) or via the TensorFlow-DirectML-Plugin.

Verify GPU Recognition

import tensorflow as tf
print(tf.config.list_physical_devices('GPU'))
If TensorFlow detects your GPU, you should see at least one physical device mentioned in the results.

Common TensorFlow Errors**

  • DLL load failed: This usually means cuDNN or CUDA isn’t set up properly in your system PATH.
  • Could not load dynamic library: This often happens when there’s a mismatch between the installed CUDA/cuDNN versions and those expected by TensorFlow.

PyTorch CUDA Configuration

Install PyTorch

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
This command will install the latest compatible versions of torch, torchvision, and torchaudio built for CUDA 12.1. You must make sure you have the appropriate CUDA 12.1 drivers installed on your system for optimal performance.

Check GPU Availability

import torch
print(torch.cuda.is_available()) # Should return True if everything is correct
A True output means PyTorch can recognize your GPU.

OptiMulti-GPU Setup

If you have multiple GPUS, you can perform computations using torch.nn.DataParallel or DistributedDataParallel. To see how many GPUs PyTorch identifies, run:
torch.cuda.device_count()

Other Frameworks (MXNet, Caffee, etc.)

MXNet

First, install the GPU version:
pip install mxnet-cu``x
The placeholder cu11x should be replaced with the actual version, suchc as cu110 for CUDA 11.0 or cu113 for CUDA 11.3
Next, check how many GPUs you have acces to
import mxnet as mx
print (mx.context.num_gpus())
If you see a non-zero result, that means MXNet can access your GPUs.

Caffee

  • Usually, you’ll compile this from the source and set up your CUDA and cuDNN paths in the Makefile.config file.
  • Some users prefer to install Caffe via Conda but make sure your CUDA and cuDNN versions align with the library’s requirements.
Following these steps, you can easily set up GPU acceleration for different deep learning frameworks, taking full advantage of CUDA and cuDNN for faster training and inference.
To learn about advanced PyTorch debugging and memory management, read our article on PyTorch Memory and Multi-GPU Debugging.

Installing cuDNN with Python Wheels via pip

NVIDIA provides Python wheels for easy installation of cuDNN through pip, simplifying the integration process into Python projects. This method is particularly advantageous for those working with deep learning frameworks like TensorFlow and PyTorch.

Prerequisite

  • Python Environment: Make sure Python is installed on your system. To prevent conflicts, it’s recommended that you use a virtual environment for managing dependencies.
  • CUDA Toolkit: Install the appropriate version of the CUDA Toolkit that is compatible with both your GPU and the cuDNN version you plan to use.

Step 1: Upgrade pip and wheel

Before installing cuDNN, ensure that pip and wheel are updated to their latest versions:
python3 -m pip install --upgrade pip wheel

Step 2: Installing cuDNN

To install CUDA 12, use the following command:
python 3 -m pip install nvidia-cudnn-cu12
To install Cuda 11, use the following command:
python 3 -m pip install nvidia-cudnn-cu11
For a specific version of cuDNN (e.g. 9.x.y.z), you can specify the version number:
python3 -m pip install nvidia-cudnn-cu12==9.x.y.z

Troubleshooting Common Issues

This section outlines common issues encountered with CUDA and cuDNN and provides their causes along with respective solutions.
How to resolve CUDA/cuDNN issues?

Cuda Driver Version Insufficiency

  • Cause: The GPU driver in use is outdated compared to the version required for the CUDA Toolkit on your system.
  • Solution: Update your driver to a version that is at least as recent as recommended for your CUDA version. Afterward, restart your system and attempt the operation again.

cuDNN Library Not Found

  • Cause: The cuDNN files might be incorrectly located, or your environment variables are not set up properly.
  • Solution: Ensure that cudnn64\_x.dll (for Windows) or libcudnn.so (for Linux) is placed within the same directory as your CUDA installation. Also, verify that LD\_LIBRARY\_PATH or PATH includes the directory where these libraries reside.

Multiple CUDA Versions on the Same Machine

You can install various CUDA versions (like 10.2 and 11.8) simultaneously, but be aware of the following:
  • Path issues: Only one version can take precedence in your environment PATH.
  • Framework Configuration: Certain frameworks may default to the first nvcc they recognize.
  • Recommendation: Use environment modules or containerization techniques (like Docker) to isolate different CUDA versions.

Enviornment Variable Conflicts

You might encounter library mismatch errors if your PATH or LD_LIBRARY_PATH points to an old or conflicting CUDA version. Always check that your environment variables correspond to the correct paths for the specific CUDA/cuDNN version you plan to use.

FAQs

How to install CUDA on a GPU?

Begin by downloading and installing the latest NVIDIA GPU driver suitable for your operating system. Next, head to NVIDIA’s official website to get the CUDA Toolkit, and proceed to run the installation. Don’t forget to restart the system once you have completed the installation.

How to set up CUDA and cuDNN?

First, proceed with installing the CUDA Toolkit and downloading cuDNN from the NVIDIA Developer portal. Copy the cuDNN files into the CUDA directories (namely, bin, include, and lib) and adjust environment variables as required.

Can I use CUDA on my GPU?

As long as your GPU is an NVIDIA GPU that supports CUDA. You can verify this by referring to NVIDIA’s official list or inspecting your GPU’s product page details.

How to install CUDA 11.8 and cuDNN?

Start with a compatible driver installation, then download the CUDA 11.8 installer. Afterward, download cuDNN version 8.x that aligns with CUDA 11.8, and make the cuDNN files placed in the correct directories.

How do I check if my GPU is CUDA enabled?

On a Windows system, you can check for an NVIDIA GPU in the Device Manager. For Linux users, execute the command: lspci | grep \-i nvidia. Finally, compare your GPU model with the specifications listed on NVIDIA’s website.

Is CUDA a GPU driver?

No, CUDA itself is a parallel computing platform. You need to install the NVIDIA driver to communicate properly with GPU hardware.

What are CUDA and cuDNN used for in AI and ML?

CUDA enables parallel computing tasks on the GPU, while cuDNN optimizes deep neural network processes, such as convolutions.

How do I check if my GPU supports CUDA?

Find your GPU model on the NVIDIA Developer site or consult the list of CUDA-enabled GPUs. Generally, most modern NVIDIA GPUs support CUDA.

What is the difference between the CUDA Toolkit and cuDNN?

The CUDA Toolkit provides essential libraries, a compiler, and tools necessary for general GPU computing, while cuDNN is a specialized library for deep neural network operations.

How do I resolve the cuDNN library errors that were not found?

Make sure that the cuDNN files (like .dll for Windows or .so for Linux) are correctly copied in the designated folders (e.g., /usr/local/cuda/lib64 on Linux), and verify that your environment variables point to those directories.

Can I install multiple versions of CUDA on the same machine?

Yes, it is possible. Each version should reside in its respective directory (for example, C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2, v11.8, etc.). You will need to update the PATH and other environmental variables when switching between versions.

Conclusion

Installing CUDA and cuDNN is essential in unlocking the full capabilities of NVIDIA GPUs for tasks like deep learning, scientific simulations, and processing large datasets. By adhering to the detailed instructions provided in this guide, you’ll streamline the installation of CUDA cuDNN on both Windows and Ubuntu. This will result in accelerated model training, optimized data handling, and improved computational power.
When properly configured, with version compatibility checks and performance optimization, your GPU environment will be ready to support renowned frameworks such as TensorFlow, PyTorch, and MXNet. Whether you’re a beginner or have advanced knowledge, using CUDA and cuDNN can boost your efficiency. This will allow you to approach complex AI and machine learning challenges with improved speed and efficiency.

References

"James Skelton" / 2025-02-14 3 months ago / 未收藏/ DigitalOcean Community Tutorials发送到 kindle
DeepSeek AI, the rising star of the AI world from Hangzhou China, has been one of the hottest topics around the past few weeks. This is largely thanks to the incredible performance of their R1 series of models, which offer comparable reasoning capabilities to OpenAI O1 at a fraction of the training cost. The popularity of DeepSeek R1 has brought open-source models surging back to the forefront of the general consciousness.
More recently, DeepSeek also released their newest version of the autoregressive framework Janus, Janus Pro. Janus-Pro is a unified understanding and generation Multimodal Large Language Model that is capable of interpreting and generating both image and text data. In it does this by “by decoupling visual encoding into separate pathways, while still utilizing a single, unified transformer architecture for processing. The decoupling not only alleviates the conflict between the visual encoder’s roles in understanding and generation, but also enhances the framework’s flexibility.”
Follow along with this article to learn how Janus Pro works, how it compares to other multimodal LLMs, and how to run Janus Pro on a DigitalOcean GPU Droplet.

Prerequisites

Python: Experience with Python code is required to follow along Deep Learning: This article will explore advanced concepts in deep learning

The Janus Pro Framework

The Janus model family is based on the autoregressive transformer, which determines the probabilistic correlation between elements in a sequence to infer the following element. The unique approach of Janus is the decoupling of the encoding methods to convert the raw inputs into features. These are then processed by an unified autoregressive transformer. In practice, this allows for the creation of a combined model for both visual understanding and image synthesis.
In this section, we will explore what allowed the Janus architecture and framework to achieve such great results.

Janus Pro Architecture

Janus pro architecture
The core architecture of Janus Pro is the same as its predecessor, Janus. In Janus models, the defining characteristic of their processing is the decoupled visual encoding for multimodal vision and generation. The independent encoders are then used to interpret the features from the inputs. They are then processed by a unified autoregressive transformer.
For multimodal understanding, they use the SigLIP (Sigmoid Loss for Language Image Pre-Training) to extract the coarse features from the image. These features are then flattened to a 1-dimensional representation where an adaptor maps the features to the input space of the LLM.
For generation tasks, the VQ tokenizer converts the image features into discrete IDs and flattens the sequence to a single dimension. “They then use a generation adaptor to map the codebook embeddings for each ID into the input space of the LLM. They then concatenate these feature sequences to form a multimodal feature sequence, which is subsequently fed into the LLM for processing. The built-in prediction head of the LLM is utilized for text predictions in both the pure text understanding and multimodal understanding tasks, while a randomly initialized prediction head is used for image predictions in the visual generation task. The entire model adheres to an autoregressive framework without the need for specially designed attention masks” (Source).

Janus Pro Training Strategy

janus stages
To achieve these results, Janus Pro used an optimized version of the three stage training process from Janus. Stage 1 trains the adaptors and image head, stage 2 is the unified pretraining of everything but the generation and understanding encoder, and stage 3 supervised finetuning of the understanding encoder. Let’s look at these in more detail.
In Stage 1, the goal is to train a connection between the visual and textual features in the embedding space. This functionally facilitates the LLMs to understand image elements and have the beginnings of image generation capabilities. During this stage, the model is frozen with only the understanding adaptor, generation adaptor and image head being updated. In Janus Pro, this process is extended for more training steps. More training on ImageNet allowed for model pixel dependence and superior image generation capabilities on limited categories of images. (Source)
In Stage 2, the LLM is unfrozen and they perform unified pretraining on a multimodal corpus to allow Janus to learn and understand pure text data, multimodal understanding data, and visual generation data (source ). In Janus Pro, they ignore ImageNet completely at this stage, and instead use text-to-image data to generate images based on dense descriptions. This improved both training efficiency and overall robustness of the image generation capabilities. (Source)
In Stage 3, all parameters of the pretrained model, except the generation encoder, are fine-tuned with instruction tuning data to enhance the model’s instruction-following and dialogue capabilities. This refines its capabilities to better follow that of conventional instruction-response LLMs. To ensure consistent improvement across all modalities, the fine-tuning data consists of multimodal data, pure text data, and text to image data. In Janus Pro, they use an adjusted ratio of this data split. They found that slightly reducing the text-to-image data proportion actually improves multimodal performance without affecting generation capabilities significantly.
It is thanks to this tripartite training paradigm that Janus Pro is capable of such a wide variety of deep learning tasks. In our experiments, we found the model to be extremely capable for any of the tasks we gave it including instruction-response, multimodal understanding of image data, and text-to-image generation.

Janus Pro running on DigitalOcean GPU Droplets

To get started, you will need a DigitalOcean GPU Droplet. If you haven’t created one before, we recommend following the steps shown in this tutorial, the documentation, or by watching the video above.
Once your GPU Droplet is set up, open the web console or SSH in using your local terminal. Then, paste the following code into the terminal window.
apt get install -y git-lfs pip3
git-lfs clone https://huggingface.co/spaces/deepseek-ai/Janus-Pro-7B
pip install -r requirements.txt spaces omegaconf einops timm spaces torchvision attrdict 
python app.py - -share
This will download the Janus Pro model into the HuggingFace cache, and then launch the web application run by Gradio. This can be accessed anywhere on any browser by using the shared, public link.
janus explaining a meme
To get started, upload an image to the GUI. Then ask the GUI a question about the image. For example, we found the model quite capable at interpreting memes and scientific equations. It’s also incredible for image captioning.
Next, tab over to the image generator and try your hand at the generation. While nowhere near the capabilities of FLUX or Stable Diffusion, we are impressed by the versatility of the model.
Overall, we found Janus Pro to be a very capable multimodal understanding LLM and with image generation capabilities.

Closing Thoughts

In conclusion, Janus Pro is an incredibly interesting model. It is capable as both an LLM, visual understanding model, and image generator. We look forward to seeing how future efforts with autoregressive models continue to advance the field.
"Pankaj" / 2025-02-14 3 months ago / 未收藏/ DigitalOcean Community Tutorials发送到 kindle

Introduction

Constructor in java is used to create the instance of the class. Constructors are almost similar to methods except for two things - its name is the same as the class name and it has no return type. Sometimes constructors are also referred to as special methods to initialize an object.

Constructor in Java

Whenever we use new keyword to create an instance of a class, the constructor is invoked and the object of the class is returned. Since constructor can only return the object to class, it’s implicitly done by java runtime and we are not supposed to add a return type to it. If we add a return type to a constructor, then it will become a method of the class. This is the way java runtime distinguish between a normal method and a constructor. Let’s assume we have following code in Employee class.
public Employee() {
 System.out.println("Employee Constructor");
}

public Employee Employee() {
 System.out.println("Employee Method");
 return new Employee();
}
Here the first one is a constructor, notice that there is no return type and no return statement. The second one is a normal method where we are again calling the first constructor to get Employee instance and return it. It’s recommended to not have method name same as the class name because it creates confusion.

Types of Constructor in Java

There are three types of constructor in java.
  1. Default Constructor
  2. No-Args constructor
  3. Parameterized constructor
Let’s look into all these constructor types with example programs.

Default Constructor in Java

It’s not required to always provide a constructor implementation in the class code. If we don’t provide a constructor, then java provides default constructor implementation for us to use. Let’s look at a simple program where default constructor is being used since we will not explicitly define a constructor.
package com.journaldev.constructor;

public class Data {

 public static void main(String[] args) {
  Data d = new Data();
 }
}
  1. Default constructor only role is to initialize the object and return it to the calling code.
  2. Default constructor is always without argument and provided by java compiler only when there is no existing constructor defined.
  3. Most of the time we are fine with default constructor itself as other properties can be accessed and initialized through getter setter methods.

No-Args Constructor

Constructor without any argument is called a no-args constructor. It’s like overriding the default constructor and used to do some pre-initialization stuff such as checking resources, network connections, logging, etc. Let’s have a quick look at the no-args constructor in java.
package com.journaldev.constructor;

public class Data {
        //no-args constructor
 public Data() {
  System.out.println("No-Args Constructor");
 }
 public static void main(String[] args) {
  Data d = new Data();
 }
}
Now when we will call new Data(), then our no-args constructor will be called. Below image illustrates this behavior, check the console output of the program. no args constructor in java

Parameterized Constructor

Constructor with arguments is called parameterized constructor. Let’s look at the example of parameterized constructor in java.
package com.journaldev.constructor;

public class Data {

 private String name;

 public Data(String n) {
  System.out.println("Parameterized Constructor");
  this.name = n;
 }

 public String getName() {
  return name;
 }

 public static void main(String[] args) {
  Data d = new Data("Java");
  System.out.println(d.getName());
 }

}
java class constructor with parameters

Constructor Overloading in Java

When we have more than one constructors, then it’s constructor overloading in java. Let’s look at an example of constructor overloading in java program.
package com.journaldev.constructor;

public class Data {

 private String name;
 private int id;

 //no-args constructor
 public Data() {
  this.name = "Default Name";
 }
 //one parameter constructor
 public Data(String n) {
  this.name = n;
 }
 //two parameter constructor
 public Data(String n, int i) {
  this.name = n;
  this.id = i;
 }

 public String getName() {
  return name;
 }

 public int getId() {
  return id;
 }

 @Override
 public String toString() {
  return "ID="+id+", Name="+name;
 }
 public static void main(String[] args) {
  Data d = new Data();
  System.out.println(d);
  
  d = new Data("Java");
  System.out.println(d);
  
  d = new Data("Pankaj", 25);
  System.out.println(d);
  
 }

}

Private Constructor in Java

Note that we can’t use abstract, final, static and synchronized keywords with constructors. However we can use access modifiers to control the instantiation of class object. Using public and default access is still fine, but what is the use of making a constructor private? In that case any other class won’t be able to create the instance of the class. Well, a constructor is made private in case we want to implement singleton design pattern. Since java automatically provides default constructor, we have to explicitly create a constructor and keep it private. Client classes are provided with a utility static method to get the instance of the class. An example of private constructor for Data class is given below.
// private constructor
private Data() {
 //empty constructor for singleton pattern implementation
 //can have code to be used inside the getInstance() method of class
}

Constructor Chaining in Java

When a constructor calls another constructor of the same class, it’s called constructor chaining. We have to use this keyword to call another constructor of the class. Sometimes it’s used to set some default values of the class variables. Note that another constructor call should be the first statement in the code block. Also, there should not be recursive calls that will create an infinite loop. Let’s see an example of constructor chaining in java program.
package com.journaldev.constructor;

public class Employee {

 private int id;
 private String name;
 
 public Employee() {
  this("John Doe", 999);
  System.out.println("Default Employee Created");
 }
 
 public Employee(int i) {
  this("John Doe", i);
  System.out.println("Employee Created with Default Name");
 }
 public Employee(String s, int i) {
  this.id = i;
  this.name = s;
  System.out.println("Employee Created");
 }
 public static void main(String[] args) {

  Employee emp = new Employee();
  System.out.println(emp);
  Employee emp1 = new Employee(10);
  System.out.println(emp1);
  Employee emp2 = new Employee("Pankaj", 20);
  System.out.println(emp2);
 }

 @Override
 public String toString() {
  return "ID = "+id+", Name = "+name;
 }
 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

}
I have overridden the toString() method to print some useful information about Employee object. Below is the output produced by above program.
Employee Created
Default Employee Created
ID = 999, Name = John Doe
Employee Created
Employee Created with Default Name
ID = 10, Name = John Doe
Employee Created
ID = 20, Name = Pankaj
Notice how one constructor is being called from another constructor, that’s called constructor chaining process.

Java Super Constructor

Sometimes a class is inherited from a superclass, in that case, if we have to call superclass constructor then we can do it using super keyword. Let’s have a look at an example of using super class constructor. Note that super constructor call should be the first statement in the child class constructor. Also when instantiating child class constructor, java first initializes the super class and then child class. So if the super class constructor is not explicitly called then default or no-args constructor is called by java runtime. Let’s understand these concepts through some example program. Let’s assume we have two classes like below.
package com.journaldev.constructor;

public class Person {

 private int age;

 public Person() {
  System.out.println("Person Created");
 }

 public Person(int i) {
  this.age = i;
  System.out.println("Person Created with Age = " + i);
 }

}
package com.journaldev.constructor;

public class Student extends Person {

 private String name;

 public Student() {
  System.out.println("Student Created");
 }

 public Student(int i, String n) {
  super(i); // super class constructor called
  this.name = n;
  System.out.println("Student Created with name = " + n);
 }

}
Now if we create a Student object like below;
Student st = new Student();
What will be the output produced? The output of the above code will be:
Person Created
Student Created
So the call went to the no-args constructor of Student class since there was no super call in the first statement the no-args or default constructor of Person class is called. Hence the output. What if we are using parameterized constructor of Student class as Student st = new Student(34, "Pankaj");, the output will be:
Person Created with Age = 34
Student Created with name = Pankaj
Here the output is clear because we are explicitly calling superclass constructor, so Java doesn’t need to do any extra work from their side.

Java Copy Constructor

Java copy constructor takes the object of the same class as an argument and creates a copy of it. Sometimes we need a copy of another object to do some processing. We can do this by following ways:
  1. implement cloning
  2. providing a utility method for deep copy of the object.
  3. Having a copy constructor
Now let’s see how to write a copy constructor. Suppose we have a class Fruits like below.
package com.journaldev.constructor;

import java.util.ArrayList;
import java.util.List;

public class Fruits {

 private List<String> fruitsList;

 public List<String> getFruitsList() {
  return fruitsList;
 }

 public void setFruitsList(List<String> fruitsList) {
  this.fruitsList = fruitsList;
 }

 public Fruits(List<String> fl) {
  this.fruitsList = fl;
 }
 
 public Fruits(Fruits fr) {
  List<String> fl = new ArrayList<>();
  for (String f : fr.getFruitsList()) {
   fl.add(f);
  }
  this.fruitsList = fl;
 }
}
Notice that Fruits(Fruits fr) is performing a deep copy to return the copy of the object. Let’s look at a test program to understand why it’s better to have copy constructor to copy an object.
package com.journaldev.constructor;

import java.util.ArrayList;
import java.util.List;

public class CopyConstructorTest {

 public static void main(String[] args) {
  List<String> fl = new ArrayList<>();
  fl.add("Mango");
  fl.add("Orange");

  Fruits fr = new Fruits(fl);

  System.out.println(fr.getFruitsList());

  Fruits frCopy = fr;
  frCopy.getFruitsList().add("Apple");

  System.out.println(fr.getFruitsList());

  frCopy = new Fruits(fr);
  frCopy.getFruitsList().add("Banana");
  System.out.println(fr.getFruitsList());
  System.out.println(frCopy.getFruitsList());

 }

}
The output of the above program is:
[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]
Notice that when copy constructor is used, both the original object and its copy are unrelated to each other and any modifications in one of them will not reflect into other. That’s all for the constructor in java.

Advanced Use Cases of Constructors in Frameworks

Constructors in Spring and Hibernate

Constructors play a crucial role in Java frameworks such as Spring and Hibernate:
  1. Spring Framework: Uses constructor injection to enforce dependency management, particularly for immutable beans.
       // This is a Spring component class for UserService.
       // It is used to manage the user data and operations.
       @Component
       public class UserService {
          // This is a final field for UserRepository.
          // It is used to access the user data.
          private final UserRepository userRepository;
    
          // This is a constructor for UserService.
          // It is used to initialize the UserRepository.
          @Autowired
          public UserService(UserRepository userRepository) {
             this.userRepository = userRepository;
          }
       }
    
  2. Hibernate ORM: Hibernate ORM uses default (no-argument) constructors to instantiate objects when retrieving data from the database. The default constructor is essential because Hibernate creates objects using reflection and needs a way to instantiate an entity before setting its properties.
    
    import jakarta.persistence.*;
    
    @Entity
    @Table(name = "users")
    public class User {
       
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
       
       private String name;
       private String email;
    
       // **Default Constructor (Required by Hibernate)**
       public User() {
       }
    
       // **Parameterized Constructor (For Custom Initialization)**
       public User(String name, String email) {
          this.name = name;
          this.email = email;
       }
    
       // Getters and Setters
       public Long getId() {
          return id;
       }
    
       public void setId(Long id) {
          this.id = id;
       }
    
       public String getName() {
          return name;
       }
    
       public void setName(String name) {
          this.name = name;
       }
    
       public String getEmail() {
          return email;
       }
    
       public void setEmail(String email) {
          this.email = email;
       }
    
    }
    

FAQs

1. What is a constructor in Java?

A constructor in Java is a special method that initializes objects. It is automatically called when an object is instantiated. Unlike regular methods, constructors do not have a return type, not even void. Constructors help in setting up initial values for object properties and ensure that an object is in a valid state from the beginning.

2. What are the four types of constructors in Java?

Java supports different types of constructors, which include:
  • Default Constructor – A no-argument constructor that initializes instance variables with default values.
  • Parameterized Constructor – Allows passing arguments to set initial values for object properties.
  • Copy Constructor – Creates a new object by copying an existing object’s values.
  • Private Constructor – Restricts object instantiation from outside the class, commonly used in the Singleton pattern. You can refer to this tutorial on Builder Design Pattern in Java.

3. Why use a constructor instead of a method?

Constructors and methods differ in purpose. A constructor is called automatically when an object is created, ensuring proper initialization. Methods, on the other hand, must be explicitly invoked and are used to define behavior. Additionally, a constructor cannot return a value, whereas methods can return results based on business logic.
Feature Constructors Methods
Purpose Initialize objects Define behavior
Invocation Automatically called Explicitly invoked
Return Type No return type Can return a value
Overloading Yes Yes
Overriding No Yes
Access Modifiers Yes Yes
Static No Yes
Final No Yes
Abstract No Yes
Synchronized No Yes

4. What is the difference between a constructor and an object in Java?

  • A constructor is a block of code within a class that initializes an object.
  • An object is an instance of a class created using the new keyword.
  • The constructor is executed only once per object creation, while objects persist in memory and can be used multiple times.
Feature Constructors Objects
Purpose Initializes objects Represents an instance of a class
Creation Executed once per object creation Created using the new keyword
Duration Executes once Persists in memory and can be used multiple times
Role Sets initial state of an object Represents a real-world entity or concept

5. What is the benefit of using a constructor in Java?

Constructors provide multiple benefits, including:
  • Automatic Object Initialization: Ensures an object starts in a valid state.
  • Code Reusability: Reduces redundancy by centralizing initialization logic.
  • Encapsulation & Immutability: Used in frameworks like Spring and Hibernate to enforce dependency injection and configuration settings. You can refer to this tutorial on Java Reflection Example.

Conclusion

Constructors are an essential part of Java programming, playing a crucial role in object initialization and class design. From simple default constructors to more advanced constructor chaining and dependency injection in frameworks like Spring and Hibernate, understanding how constructors work can help developers build robust, maintainable applications.
By leveraging constructors effectively, you can enforce immutability, promote reusability, and optimize performance.
You can check out these related tutorials to learn more:
"Adrien Payong" / 2025-02-14 3 months ago / 未收藏/ DigitalOcean Community Tutorials发送到 kindle

Introduction

The advent of deep learning has changed the landscape of artificial intelligence. This shift has improved many areas, including image analysis, natural language understanding, customized recommendations, and self-driving technology. A key contributor to these developments is the suite of libraries and frameworks that enable the design, training, and deployment of complex neural networks. Among these, two standout frameworks emerge as essential tools for programmers: PyTorch and TensorFlow.
This article will provide a comprehensive comparison of these two frameworks by exploring their backgrounds, structural differences, user-friendliness, performance benchmarks, and community engagement.

What is PyTorch

PyTorch stands out as an open-source library for machine learning, characterized by its user-friendly Pythonic interface that enhances debugging and customization. Its dynamic computation graph and flexible architecture make it particularly advantageous for research and prototyping. However, compared to TensorFlow, its ecosystem is less extensive, and it tends to be less optimized for large-scale production environments.

What is TensorFlow

TensorFlow is a powerful open-source framework tailored for machine learning and numerical computations, using static computational graphs. It provides efficient production deployment, a wide range of toolkits, and is particularly suited for mobile and embedded devices. However, Despite its scalability, TensorFlow has a steeper learning curve. It also offers less flexibility for experimental research when compared to PyTorch.

Prerequisites

  • Understanding the principles behind neural networks, the training process, and advanced deep learning frameworks, such as Convolutional Neural Networks and Transformers.
  • Hands-on experience in Python programming, alongside essential libraries like NumPy and popular frameworks such as PyTorch and TensorFlow, including APIs like Keras and PyTorch Lightning.
  • Knowledge of GPUs, TPUs, CUDA, mixed-precision training strategies, and using debugging tools like TensorBoard to enhance performance.
  • Understanding model deployment systems like TensorFlow Serving and TorchServe, cloud platforms including AWS, Google Cloud Platform, and Microsoft Azure.

Historical Context and Evolution

Originally launched by the Google Brain team in 2015, TensorFlow rapidly became the preferred framework for deep learning. This was mainly due to its focus on scalability and deployment capabilities in real-world applications.
In contrast, PyTorch emerged in 2016, providing a fresh, Python-oriented perspective on the Torch framework developed by Facebook’s AI Research division. With its user-friendly interface and adaptable computation graph, PyTorch quickly became popular among researchers.
Both frameworks have evolved considerably over time. The introduction of TensorFlow 2.0 in 2019 represented an important transition towards enhanced usability and eager execution. This improvement successfully tackles many of the issues highlighted in earlier iterations.
At the same time, PyTorch has persistently improved its features and broadened its ecosystem, progressively matching TensorFlow in readiness for production use.

Dynamic vs. Static Graphs

One of the frequent points of comparison between PyTorch and TensorFlow lies in their approach to graph management—the difference between dynamic and static graphs. Although TensorFlow 2.x embraces eager execution, enabling a more imperative programming approach, it also offers a legacy and optimizations geared towards a static graph framework.
Graph Models in AI Framework

Dynamic Graph Advantages

For instance, if a developer wants a specific layer to perform differently during each forward pass, PyTorch’s dynamic graph feature allows instant experimentation without requiring distinct graph definitions or session executions.
For example if we consider the following code snippet:
import torch
y = torch.tensor([2.0, 3.0]); print(y**3 if y.sum() > 3 else y/3)
PyTorch builds the computation graph dynamically, allowing you to incorporate logical branches (if x.sum() > 3) directly in Python, with interpretation occurring at runtime.

Static Grpah Advantages

On the other hand, TensorFlow’s static graph model—while improved with eager execution in its recent iterations—holds the capacity to optimize performance once the graph is defined. The system can analyze, optimize, and transform the entire graph before execution.
Using a static graph also improves efficiency in production settings. For example, with TensorFlow Serving, you can freeze a graph and rapidly deploy it in a high-performance context.
Let’s consider the code below in Tensorflow 2.x:
import tensorflow as tf

  @tf.function
  def operation(y, z):
      return tf.where(tf.reduce_sum(y) > 3, y**3, y/3)
  y = tf.constant([2.0, 3.0, 4.0])  
  z = tf.constant([3.0, 4.0])
  res = operation(y, z)
  print(res.numpy())
Using the tf.function decorator converts this Python function internally into a static graph. Although TensorFlow 2.x allows for eager execution, tf.function compiles operations into a static graph for potential optimizations. This demonstrates the legacy of TensorFlow’s static graph architecture.

Bridging Dynamic and Static Graph Execution

PyTorch uses TorchScript, which connects dynamic graph execution with the capacity to trace or script models into a more defined, static structure. This approach not only provides potential performance gains but also simplifies deployment while keeping the dynamic experience required for prototyping.
TensorFlow’s eager mode provides a developer experience akin to that of PyTorch, with minor variations in debugging and architectural management. However, it remains possible to create a static graph for production purposes.
Below is a brief illustration of how to use TorchScript to convert a PyTorch function into a static traced graph, all while starting from a dynamic (eager) context:
import torch
  script = torch.jit.trace(torch.nn.Linear(3, 2), torch.randn(1, 3));
  print(script.code)
  • torch.jit.trace( ) monitors your model (torch.nn.Linear(3, 2)) using a sample tensor input (torch.randn(1, 3)).
  • script.code will display the TorchScript code that will be generated. This demonstrates the transition of PyTorch from a dynamic graph configuration to a trace-based static representation.

Development Experience and Syntax

For many developers, the main selling point of a framework is how easy it is to code day-in-and-day-out.

Imperative vs. Declarative

PyTorch predominantly follows an imperative programming style, which lets you write code that executes commands immediately. This makes identifying errors straightforward, as Python’s stack traces highlight issues directly as they occur. This approach is familiar with users accustomed to traditional Python or libraries like NumPy.
Pytorch and Tensorflow 2x
On the other hand, TensorFlow 2.x adops eager execution, allowing you to write in a similar imperative manner

API Layers

It’s common for developers to use the torch.nn module or other enhanced tools such as torchvision for image-related tasks, or torchtext for processing natural language. Another higher-level framework is PyTorch Lightning, which reduces the boilerplate code involved in tasks like training loops, checkpointing, and multi-GPU support.
Keras is also recognized as a top choice for high-level APIs, allowing you to operate in a straightforward imperative manner. Using Keras, you can also take a complex approach with tf.function decorators that optimize graph optimization. Its popularity stems mainly from its ease of use, making it particularly attractive for those aiming to deploy models without unnecessary complications.

Error Messaging and Debugging

With dynamic graph execution models, error messages typically indicate the exact lines in your Python code that are causing issues. This feature is helpful for beginners or when tackling complex model structures.
Eager execution simplifies the debugging process compared to TensorFlow 1.x. Nevertheless, it is important to remember that certain errors might still be confusing when you combine eager execution with graph-based operations (via tf.function).
Let’s consider the following code:
import tensorflow as tf
  @tf.function
  def op(y): return y + "error"
  print(op(tf.constant([2.0, 3.0])))  # Triggers autograph conversion error
Output:
TypeError: Input 'y' of 'AddV2' Op has type string that does not match type float32 of argument 'x'.
The error arises instantly in the PyTorch example because it uses dynamic graph execution, meaning each operation takes place in real-time. Adding a string to a tensor is an invalid action, leading Python to issue a TypeError. This makes identifying and resolving the issue straightforward.
On the other hand, the TensorFlow example uses @tf.function, which attempts to convert the function into a static computation graph. Instead of executing the function step by step, TensorFlow compiles it beforehand.
When an invalid operation (like appending a string to a tensor) is detected, the error emerges from TensorFlow’s internal graph conversion process. This makes debugging challenging compared to the immediate and clear feedback provided by PyTorch.

Performance Considerations

In deep learning, several factors influence performance levels. Key considerations include the training speed, effective utilization of GPUs, and proficiency in handling extensive models and datasets. PyTorch and TensorFlow use GPU acceleration, utilizing NVIDIA CUDA or AMD ROCm, to boost the efficiency of tensor computations.
Which deeplearning framework to choose for optimal performance?

Low-level Optimizations

TensorFlow is a framework for large-scale and distributed training using tf.distribute, in addition to optimizing GPU performance. Its static graph model (which can be used optionally) enables improved performance through graph-level optimizations.
On the other hand, PyTorch has progressed over time, featuring well-developed backends and libraries. It supports distributed training through torch.distributed and includes improvements like torch.cuda.amp for implementing automatic mixed precision.

Mixed Precision and TPU Support

PyTorch provides a user-friendly interface for mixed-precision training, enhancing performance on GPUs equipped with Tensor Cores. While PyTorch has improved its compatibility with custom hardware, including Google’s TPUs, it does not match the native support that TensorFlow offers for these devices.
Tensorflow integrates Tensor Processing Units (TPUs), which are Google’s dedicated hardware designed to speed extensive deep learning tasks. Using TPUs typically requires minimal code changes in TensorFlow, which can be a considerable benefit if your infrastructure includes Google Cloud and TPUs.

Benchmarks

Various third-party performance tests show that PyTorch and TensorFlow perform comparably well on common tasks, particularly with single-GPU training scenarios. Nevertheless, as configurations scale to multiple GPUs or nodes, results may vary depending on model specifics, dataset size, and the use of specialized hardware.
It is essential to note that both frameworks can handle high-performance tasks effectively. Influencing factors such as slight code optimizations, optimal hardware usage, and the nature of training jobs may be more critical than the choice of the framework itself.

Ecosystem and Community Support

When choosing a deep learning framework, an essential aspect to evaluate is the supportive ecosystem that encompasses libraries, contributions from the community, educational resources, and integration with cloud services.

Model Zoos and Pretrained Models

torchvision, torchtext, torchaudio, along with Hugging Face’s Transformers library, provide PyTorch implementations across various domains such as natural language processing, computer vision, and audio analysis.
Some research organizations regularly publish state-of-the-art model checkpoints in the PyTorch format, strengthening its ecosystem.
On the other hand, TensorFlow features the tf.keras.applications module and the TensorFlow Model Garden, which highlight several pretrained models. While Hugging Face Transformers are also available for TensorFlow, PyTorch is slightly more prevalent among community-shared models.

Research Community

Many researchers prefer PyTorch due to its intuitive interface and dynamic computation graph features. It’s common to see academic research papers and early versions of new algorithms being published in PyTorch before any other framework.
Choose the best deep learning framework for research and development
Meanwhile, TensorFlow continues to have a strong presence in the research community, largely owing to its backing by Google and its proven reliability.
Improvements to the user experience in TensorFlow 2.x have drawn some researchers back into the fold. Nonetheless, PyTorch remains the framework of choice for many top AI research labs when developing and launching new model architectures.

Deployment and Production Pipelines

When choosing the right deep learning framework, it’s essential to consider not just the model training aspect but also the ease of model deployment. Modern AI applications often demand capabilities for real-time inference, support for edge devices, and the ability to scale across multiple server clusters.
Deployment and production pipeline APIS

TensorFlow Serving

TensorFlow Serving is a recognized solution for deploying models created with TensorFlow. You can “freeze” your models or save them in the ``SavedModel` format, allowing quick loading into TensorFlow Serving for rapid and reliable inference. This method not only supports high scalability but also fits within a microservices architecture.
Additionally, TensorFlow provides comprehensive features for monitoring, managing versions, and conducting A/B testing. This makes it a preferred choice for enterprise applications requiring reliable and stable deployments.

TorchServe

Built collaboratively by Facebook and Amazon, TorchServe provides a similar deployment experience for PyTorch models. It’s specifically designed for high-performance inference and simplifies integration with AWS services, such as Elastic Inference and Amazon SageMaker.
Although it may not have reached the maturity of TensorFlow Serving, TorchServe is evolving with features like multi-model serving, version management, and advanced analytics.

Cross-Framework Standardization with ONNX

The Open Neural Network Exchange (ONNX) is an open standard for representing deep learning models. You can develop a model with PyTorch, export it to ONNX format, and then perform inference across various runtimes or hardware accelerators that support it.
Converting TensorFlow models to ONNX is also possible, though it does come with certain limitations.

Mobile and Edge Deployments

LiteRT is used to run inference on devices like Android and iOS. It is specifically optimized for resource-constrained environments through techniques such as quantization and pruning.
ExecuTorch is a strong alternative for running PyTorch models on mobile devices. While LiteRT has been more established in the field, ExecuTorch solutions are gaining traction as its user base grows.

Extensibility, Tooling, and Integration

Deep learning frameworks usually don’t operate in isolation; they frequently collaborate with a variety of supportive tools for tasks like data processing, model monitoring, hyperparameter tuning, and beyond.
Extensibility, Tooling, and Integration

Integration with Data Libraries

  • PyTorch: This popular framework is extensively compatible with various Python data libraries like Pandas and NumPy. The DataLoader API allows customization for custom datasets, while additional tools such as DALI (NVIDIA Data Loading Library) further boost data processing efficiency.
  • TensorFlow: It features tf.data, an API designed for developing optimized input pipelines that handle large datasets, enabling parallel I/O operations. With functions like map, shuffle, and prefetch, tf.data can optimize GPU-based data preprocessing.
  • TensorBoard: Originally for TensorFlow, this powerful tool provides real-time insights and visualizations of training metrics, model structures, weight distributions, and embeddings. Support for PyTorch has been extended through plugins like tensorboardX or by means of direct integration (e.g., torch.utils.tensorboard).
  • Weights & Biases, Neptune.ai, Comet, and several other experiment tracking tools are seamlessly integrated with both frameworks, improving experiment management capabilities.

Hyperparameter Tuning

  • Frameworks such as Optuna, Ray Tune, and Hyperopt enable smooth integration with PyTorch and TensorFlow, enabling distributed searches for hyperparameters

Cloud Integration

  • Google Cloud Platform (GCP): GCP’s AI Platform provides support for training and deploying TensorFlow models. Additionally, GCP supports PyTorch through various means, including custom training jobs and the use of Vertex AI.
  • AWS: This platform provides support for both TensorFlow and PyTorch, with SageMaker offering pre-configured Docker images and capabilities for distributed training.
  • Microsoft Azure: Azure Machine Learning offers similar functionalities, extending support for TensorFlow and PyTorch environments.
  • DigitalOcean: Ideal for running applications built in either PyTorch or TensorFlow, it provides a wealth of resources for setting up and optimizing your machine learning environment.

So, Which Framework is Better?

The choice of the framework will depend on your project requirements, team expertise, and intended use case:

Choose PyTorch if…

  • You prioritize quick prototyping and a highly Pythonic programming style.
  • Your team is engaged in pioneering research where dynamic graph capabilities simplify experimentation.
  • You prefer the clear debugging experience offered by an imperative programming style.
  • You intend to extensively use the PyTorch ecosystem, including Hugging Face Transformers for NLP or advanced computer vision libraries.

Choose TensorFlow if…

  • Your end-to-end artificial intelligence pipeline depends on Google’s ecosystem or you’re planning deployment on Google Cloud Platform and using TPUs.
  • You require a well-established production pipeline, including TensorFlow Serving, TensorFlow Lite, or TensorFlow.js, coupled with robust support for large-scale enterprise solutions.
  • You appreciate the high-level Keras API for rapid model development and search for a well-structured environment for advanced optimization using static graphs (where advantageous).

Bridging Strategies

With frameworks like ONNX, it is possible to combine and interchange frameworks. However, certain features specific to each framework may not always integrate seamlessly.
Numerous organizations adopt a ‘two-framework strategy,’ using PyTorch for research and experimentation, subsequently porting stable models to TensorFlow for production. This approach can be effective, but it may introduce additional overhead in code maintenance.

FAQs

What are the main differences between PyTorch and TensorFlow?

PyTorch operates on an imperative model, often referred to as eager execution, which aligns with the expectations of Python programmers. In contrast, TensorFlow originally used a static computation graph but has since evolved to adopt eager execution as its default mode starting from TensorFlow 2.x.

Which framework is more suitable for research and prototyping?

Researchers frequently preferred PyTorch for its dynamic computation graph and Python-friendly syntax, which supports rapid debugging and modifications.

How does model deployment differ between PyTorch and TensorFlow?

TensorFlow provides options like TensorFlow Serving, LiteRT, and TensorFlow.js for deploying models in production, whereas PyTorch offers TorchServe, ONNX compatibility, and mobile deployment options such as PyTorch Mobile.

Which framework has better GPU and TPU support?

While both frameworks leverage CUDA for GPU support, TensorFlow provides improved native capabilities for Google’s TPUs, making it the preferred choice for tasks involving TPU usage.

Can I use both frameworks in the same project?

Absolutely! Through ONNX (Open Neural Network Exchange), you can convert models between PyTorch and TensorFlow, though certain features specific to each framework may not always translate seamlessly.

Conclusion

In deep learning, PyTorch and TensorFlow are at the forefront, each offering unique advantages that cater to developer needs and organizational requirements.
Many developers see PyTorch as the quickest path from idea to operational model. TensorFlow is often recognized as an all-encompassing option for large-scale deployment.
Fortunately, selecting either framework will not impede your journey. Both offer powerful features and are supported by vibrant communities along with extensive tools to meet various needs. No matter which path you take, a landscape full of breakthroughs in deep learning is within your reach.

Resources

"asinghwalia" / 2025-02-14 3 months ago / 未收藏/ DigitalOcean Community Tutorials发送到 kindle

Introduction

Receiving user input is fundamental in Python programming, allowing developers to build interactive applications. Whether you’re working on command-line scripts, GUI-based applications, or web applications, handling user input correctly ensures efficient and secure software.
In this tutorial you will learn various ways to receive user input in Python, including the input() function, command-line arguments, GUI-based input handling, and best practices for validation and error handling.

1. Using the input() Function

The simplest way to receive user input is through Python’s built-in input() function. It reads a string from the keyboard and returns it.
Example:
name = input("Enter your name: ")
print("Hello, " + name + "!")

Taking Integer Input

Since input() returns a string, you must convert it to an integer when necessary:
age = int(input("Enter your age: "))
print("You are", age, "years old.")

Handling Float Input

When dealing with numerical inputs that require decimal precision, such as prices or measurements, you need to convert the user’s input to a floating-point number. This is achieved using the float() function, which converts a string to a floating-point number.
Here’s an example of how to handle float input in Python:
price = float(input("Enter the price: "))
print("The price is", price)

Validating User Input

When dealing with user input, it’s essential to handle potential errors that may occur when users enter invalid data. One effective way to do this is by using try-except blocks. This approach allows you to catch and manage exceptions that might be raised during the input process, ensuring your program remains robust and user-friendly.
while True:
    try:
        number = int(input("Enter an integer: "))
        break
    except ValueError:
        print("Invalid input! Please enter a valid integer.")
For learning more about handling different data types, check out our tutorial on Python Data Types.

2. Handling Multiple User Inputs

When working with user input, it’s common to require multiple values from the user. Python provides a convenient way to handle this using the split() method, which divides a string into a list of substrings based on a specified separator. In the case of user input, we can use split() to separate multiple values entered by the user.
Here’s an example of how to use split() to receive two string inputs from the user:
x, y = input("Enter two numbers separated by space: ").split()
print("First number:", x)
print("Second number:", y)
However, if you need to perform numerical operations on these inputs, you’ll need to convert them to numerical types. This can be achieved using the map() function, which applies a given function to each item of an iterable (in this case, the list of strings returned by split()). Here’s how to modify the previous example to convert the inputs to integers:
x, y = map(int, input("Enter two numbers: ").split())
print("Sum:", x + y)
By using map() to convert the inputs to integers, you can perform arithmetic operations on them as needed.

3. Reading Input from Command-Line Arguments

When executing a Python script from the command line, it’s often necessary to pass additional information or parameters to the script. This is achieved through command-line arguments, which are values provided after the script name when running it. Python’s sys.argv method allows you to access these arguments within your script.
Here’s an example of how to use sys.argv to read command-line arguments:
Save the following script as script.py:
import sys

# sys.argv is a list that contains the script name and all arguments passed to it
print("Script name:", sys.argv[0])  # sys.argv[0] is the script name itself

# Check if any arguments were passed
if len(sys.argv) > 1:
    print("Arguments:", sys.argv[1:])  # sys.argv[1:] contains all arguments except the script name
To run the script with arguments, use the following command in your terminal or command prompt:
python script.py Hello World
In this example, Hello and World are the arguments passed to the script. The script will output the script name and the arguments provided.
Understanding how to work with command-line arguments is essential for creating scripts that can be easily customized or configured without modifying the script itself. This approach is particularly useful for scripts that need to perform different tasks based on user input or configuration.

4. Receiving Input in GUI Applications

When building graphical user interface (GUI) applications, Python offers a range of libraries to facilitate interactive input handling. One of the most popular and easy-to-use GUI frameworks is Tkinter, which is included in the Python standard library. Tkinter allows you to create simple GUI applications with a minimal amount of code.
Here’s an example of how to use Tkinter to receive user input in a GUI application:
import tkinter as tk

def get_input():
    # Retrieve the text entered by the user in the Entry field
    user_input = entry.get()
    # Update the Label to display the user's input
    label.config(text=f"You entered: {user_input}")

# Create the main window of the application
root = tk.Tk()
# Create an Entry field for user input
entry = tk.Entry(root)
entry.pack()  # Add the Entry field to the window
# Create a Button to trigger the get_input function
btn = tk.Button(root, text="Submit", command=get_input)
btn.pack()  # Add the Button to the window
# Create a Label to display the user's input
label = tk.Label(root, text="")
label.pack()  # Add the Label to the window
# Start the Tkinter event loop
root.mainloop()
This example demonstrates how to create a simple GUI application that prompts the user for input, processes that input, and displays it back to the user.

Best Practices for Handling User Input

When working with user input in Python, it’s essential to follow best practices to ensure your application is robust, secure, and user-friendly. User input can come in various forms, such as command-line arguments, input from files, or interactive input from users. Here are five key guidelines to keep in mind, along with example code to illustrate each point:

1. Validate Input

Validating user input is crucial to prevent errors and ensure that the data received is in the expected format. This can be achieved using try-except blocks to catch and handle exceptions that may occur during input processing. For instance, when asking the user to enter an integer, you can use a try-except block to handle cases where the user enters a non-integer value.
# This code block is designed to repeatedly prompt the user for an integer input until a valid integer is entered.
while True:  # This loop will continue indefinitely until a break statement is encountered.
    try:
        # The input() function is used to get user input, which is then passed to int() to convert it to an integer.
        # If the input cannot be converted to an integer (e.g., if the user enters a string or a float), a ValueError is raised.
        num = int(input("Enter an integer: "))
        # If the input is successfully converted to an integer, the loop is exited using the break statement.
        break
    except ValueError:
        # If a ValueError is raised, it means the input cannot be converted to an integer.
        # In this case, an error message is printed to the user, prompting them to enter a valid integer.
        print("Invalid input! Please enter a valid integer.")

# After the loop is exited, the program prints out the valid integer entered by the user.
print("You entered:", num)

2. Handle Errors Gracefully

Implementing error handling mechanisms is vital to prevent application crashes and provide a better user experience. try-except blocks can be used to catch and handle errors in a way that doesn’t disrupt the application’s flow. For example, when reading from a file, you can use a try-except block to handle cases where the file does not exist or cannot be read.
# Try to open the file "example.txt" in read mode
try:
    with open("example.txt", "r") as file:
        # Read the content of the file
        content = file.read()
        # Print the content of the file
        print("File content:", content)
# Handle the case where the file is not found
except FileNotFoundError:
    print("File not found!")
# Handle any other exceptions that may occur
except Exception as e:
    print("An error occurred:", str(e))

3. Limit Input Length

Limiting the length of user input can help prevent unexpected behavior, such as buffer overflow attacks or excessive memory usage. This can be achieved by using string slicing or other methods to truncate input strings beyond a certain length. For instance, when asking the user to enter a username, you can limit the input length to 20 characters.
# This code block prompts the user to enter a username, with a maximum length of 20 characters.
username = input("Please enter your username (maximum 20 characters): ")
# If the length of the username is greater than 20, it is truncated to 20 characters.
if len(username) > 20:
    username = username[:20]
    print("Your username has been truncated to 20 characters.")
# The program then prints the username entered by the user.
print("Your username is:", username)

4. Sanitize Input

Sanitizing user input is critical to prevent security risks, such as SQL injection or cross-site scripting (XSS). This involves removing or escaping special characters that could be used maliciously. Python’s html module provides a convenient way to escape HTML characters in user input. For example, when displaying user input on a web page, you can use html.escape() to prevent XSS attacks.
# This code block imports the 'html' module to use its 'escape' function for sanitizing user input.
import html

# It then prompts the user to enter a string using the 'input' function.
user_input = html.escape(input("Enter a string: "))

# The 'html.escape' function is used to escape any HTML characters in the user's input to prevent XSS attacks.
# This ensures that any special characters in the input are replaced with their HTML entity equivalents, making the input safe to display in a web page.

# Finally, the sanitized user input is printed to the console.
print("Sanitized input:", user_input)

5. Use Default Values

Providing default values for user input can significantly improve usability by reducing the amount of information users need to provide. This can be achieved using the or operator to assign a default value if the user input is empty or invalid. For instance, when asking the user to enter their name, you can provide a default value of “Guest” if the user does not enter a name.
# This code block prompts the user to enter their name and then prints a greeting message.
# The input function is used to get user input, and the or "Guest" part ensures that if the user doesn't enter anything, the default name "Guest" is used.
name = input("Enter your name: ") or "Guest"
# The f-string is used to format the greeting message with the user's name.
print(f"Hello, {name}!")
By following these best practices, you can ensure that your Python application handles user input in a way that is secure, efficient, and user-friendly.

FAQs

1. How do I receive user input in Python?

Receiving user input is a fundamental aspect of any interactive program. In Python, you can use the built-in input() function to prompt the user for input and store their response in a variable. Here’s a simple example:
user_input = input("Enter something: ")
print("You entered:", user_input)

2. How do I get integer input from a user in Python?

To get integer input from a user in Python, you can use a while loop to repeatedly prompt the user for input until a valid integer is entered. Here’s an example code block that demonstrates this:
# This code block repeatedly prompts the user to enter an integer until a valid integer is entered.
while True:  # This loop will continue to run indefinitely until a valid integer is entered.
    user_input = input("Enter an integer: ")  # This line prompts the user to enter an integer and stores the input in the 'user_input' variable.
    if user_input.isdigit():  # This condition checks if the user's input consists only of digits, indicating a valid integer.
        num = int(user_input)  # If the input is a valid integer, it is converted to an integer and stored in the 'num' variable.
        print("You entered:", num)  # This line prints a message to the console indicating the valid integer entered by the user.
        break  # This line breaks out of the loop, ending the program.
    else:
        print("Invalid input! Please enter a valid integer.")  # If the input is not a valid integer, this message is printed to the console, prompting the user to enter a valid integer.
This code block ensures that the program will continue to prompt the user for input until a valid integer is entered, making it a robust way to handle user input in Python.

3. Can I receive multiple user inputs at once in Python?

Yes, you can receive multiple user inputs at once in Python. One way to do this is by using the split() method to divide the input string into multiple parts based on a specified separator, such as a space. The map() function can then be used to convert each part into the desired data type, such as an integer.
Here’s an example code block that demonstrates how to receive two integer inputs from the user at once:
x, y = map(int, input("Enter two numbers separated by space: ").split())
print("First number:", x)
print("Second number:", y)

4. How can I handle command-line arguments instead of input()?

When running a Python script from the command line, you can pass arguments to the script. These arguments are stored in the sys.argv list. The first element of this list, sys.argv[0], is the script name itself. The rest of the elements are the arguments passed to the script.
Here’s an example of how you can handle command-line arguments in your Python script:
import sys

# Check if any command-line arguments were provided
if len(sys.argv) > 1:
    # Print the arguments received
    print("Command-line arguments received:", sys.argv[1:])
else:
    # Inform the user if no arguments were provided
    print("No command-line arguments provided.")
In this example, the script checks if any arguments were provided by checking the length of sys.argv. If there are more than one elements in sys.argv (i.e., at least one argument was provided), it prints the arguments received. If not, it informs the user that no arguments were provided.
This approach is particularly useful when you want to make your script more flexible and allow users to customize its behavior by passing arguments from the command line.

5. How do you take input in Python?

In Python, you can take input from the user using the input() function. This function returns a string, which can be stored in a variable for further processing. Here’s an example:
user_input = input("Please enter your name: ")
print("Hello, " + user_input + "!")
This code prompts the user to enter their name and then prints a greeting message with their name.

6. How do you take input from a variable in Python?

In Python, you cannot directly take input from a variable. Variables are used to store values, not to receive input. However, you can use a variable to store the input received from the user using the input() function. For example:
name = input("Please enter your name: ")
print("Hello, " + name + "!")
In this example, the name variable stores the input received from the user, and then it is used to print a greeting message.

7. How to input a number in Python?

To input a number in Python, you can use the input() function and convert the input string to an integer or float using the int() or float() function, respectively. Here’s an example:
age = int(input("Please enter your age: "))
print("You are " + str(age) + " years old.")
This code prompts the user to enter their age, converts the input to an integer, and then prints a message with their age.

Conclusion

This tutorial covered multiple ways to receive user input in Python, from basic terminal input to command-line arguments and GUI-based interactions. By implementing input validation and error handling, you can create robust and user-friendly Python applications.
For more Python tutorials, check out:
  1. Python Data Types
  2. Python Read, Write, and Copy Files
  3. Python Type Function
"Julia Evans" / 2025-02-13 3 months ago / 未收藏/ Julia Evans发送到 kindle
I was talking to a friend about how to add a directory to your PATH today. It’s something that feels “obvious” to me since I’ve been using the terminal for a long time, but when I searched for instructions for how to do it, I actually couldn’t find something that explained all of the steps – a lot of them just said “add this to ~/.bashrc”, but what if you’re not using bash? What if your bash config is actually in a different file? And how are you supposed to figure out which directory to add anyway?
So I wanted to try to write down some more complete directions and mention some of the gotchas I’ve run into over the years.
Here’s a table of contents:

step 1: what shell are you using?

If you’re not sure what shell you’re using, here’s a way to find out. Run this:
ps -p $$ -o pid,comm=
  • if you’re using bash, it’ll print out 97295 bash
  • if you’re using zsh, it’ll print out 97295 zsh
  • if you’re using fish, it’ll print out an error like “In fish, please use $fish_pid” ($$ isn’t valid syntax in fish, but in any case the error message tells you that you’re using fish, which you probably already knew)
Also bash is the default on Linux and zsh is the default on Mac OS (as of 2024). I’ll only cover bash, zsh, and fish in these directions.

step 2: find your shell’s config file

  • in zsh, it’s probably ~/.zshrc
  • in bash, it might be ~/.bashrc, but it’s complicated, see the note in the next section
  • in fish, it’s probably ~/.config/fish/config.fish (you can run echo $__fish_config_dir if you want to be 100% sure)

a note on bash’s config file

Bash has three possible config files: ~/.bashrc, ~/.bash_profile, and ~/.profile.
If you’re not sure which one your system is set up to use, I’d recommend testing this way:
  1. add echo hi there to your ~/.bashrc
  2. Restart your terminal
  3. If you see “hi there”, that means ~/.bashrc is being used! Hooray!
  4. Otherwise remove it and try the same thing with ~/.bash_profile
  5. You can also try ~/.profile if the first two options don’t work.
(there are a lot of elaborate flow charts out there that explain how bash decides which config file to use but IMO it’s not worth it and just testing is the fastest way to be sure)

step 3: figure out which directory to add

Let’s say that you’re trying to install and run a program called http-server and it doesn’t work, like this:
$ npm install -g http-server
$ http-server
bash: http-server: command not found
How do you find what directory http-server is in? Honestly in general this is not that easy – often the answer is something like “it depends on how npm is configured”. A few ideas:
  • Often when setting up a new installer (like cargo, npm, homebrew, etc), when you first set it up it’ll print out some directions about how to update your PATH. So if you’re paying attention you can get the directions then.
  • Sometimes installers will automatically update your shell’s config file to update your PATH for you
  • Sometimes just Googling “where does npm install things?” will turn up the answer
  • Some tools have a subcommand that tells you where they’re configured to install things, like:
    • Node/npm: npm config get prefix (then append /bin/)
    • Go: go env GOPATH (then append /bin/)
    • asdf: asdf info | grep ASDF_DIR (then append /bin/ and /shims/)

step 3.1: double check it’s the right directory

Once you’ve found a directory you think might be the right one, make sure it’s actually correct! For example, I found out that on my machine, http-server is in ~/.npm-global/bin. I can make sure that it’s the right directory by trying to run the program http-server in that directory like this:
$ ~/.npm-global/bin/http-server
Starting up http-server, serving ./public
It worked! Now that you know what directory you need to add to your PATH, let’s move to the next step!

step 4: edit your shell config

Now we have the 2 critical pieces of information we need:
  1. Which directory you’re trying to add to your PATH (like ~/.npm-global/bin/)
  2. Where your shell’s config is (like ~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish)
Now what you need to add depends on your shell:
bash instructions:
Open your shell’s config file, and add a line like this:
export PATH=$PATH:~/.npm-global/bin/
(obviously replace ~/.npm-global/bin with the actual directory you’re trying to add)
zsh instructions:
You can do the same thing as in bash, but zsh also has some slightly fancier syntax you can use if you prefer:
path=(
  $path
  ~/.npm-global/bin
)
fish instructions:
In fish, the syntax is different:
set PATH $PATH ~/.npm-global/bin
(in fish you can also use fish_add_path, some notes on that further down)

step 5: restart your shell

Now, an extremely important step: updating your shell’s config won’t take effect if you don’t restart it!
Two ways to do this:
  1. open a new terminal (or terminal tab), and maybe close the old one so you don’t get confused
  2. Run bash to start a new shell (or zsh if you’re using zsh, or fish if you’re using fish)
I’ve found that both of these usually work fine.
And you should be done! Try running the program you were trying to run and hopefully it works now.
If not, here are a couple of problems that you might run into:

problem 1: it ran the wrong program

If the wrong version of a is program running, you might need to add the directory to the beginning of your PATH instead of the end.
For example, on my system I have two versions of python3 installed, which I can see by running which -a:
$ which -a python3
/usr/bin/python3
/opt/homebrew/bin/python3
The one your shell will use is the first one listed.
If you want to use the Homebrew version, you need to add that directory (/opt/homebrew/bin) to the beginning of your PATH instead, by putting this in your shell’s config file (it’s /opt/homebrew/bin/:$PATH instead of the usual $PATH:/opt/homebrew/bin/)
export PATH=/opt/homebrew/bin/:$PATH
or in fish:
set PATH ~/.cargo/bin $PATH

problem 2: the program isn’t being run from your shell

All of these directions only work if you’re running the program from your shell. If you’re running the program from an IDE, from a GUI, in a cron job, or some other way, you’ll need to add the directory to your PATH in a different way, and the exact details might depend on the situation.
in a cron job
Some options:
  • use the full path to the program you’re running, like /home/bork/bin/my-program
  • put the full PATH you want as the first line of your crontab (something like PATH=/bin:/usr/bin:/usr/local/bin:….). You can get the full PATH you’re using in your shell by running echo "PATH=$PATH".
I’m honestly not sure how to handle it in an IDE/GUI because I haven’t run into that in a long time, will add directions here if someone points me in the right direction.

a note on source

When you install cargo (Rust’s installer) for the first time, it gives you these instructions for how to set up your PATH, which don’t mention a specific directory at all.
This is usually done by running one of the following (note the leading DOT):

. "$HOME/.cargo/env"        	# For sh/bash/zsh/ash/dash/pdksh
source "$HOME/.cargo/env.fish"  # For fish
The idea is that you add that line to your shell’s config, and their script automatically sets up your PATH (and potentially other things) for you.
This is pretty common (for example Homebrew suggests you eval brew shellenv), and there are two ways to approach this:
  1. Just do what the tool suggests (like adding . "$HOME/.cargo/env" to your shell’s config)
  2. Figure out which directories the script they’re telling you to run would add to your PATH, and then add those manually. Here’s how I’d do that:
    • Run . "$HOME/.cargo/env" in my shell (or the fish version if using fish)
    • Run echo "$PATH" | tr ':' '\n' | grep cargo to figure out which directories it added
    • See that it says /Users/bork/.cargo/bin and shorten that to ~/.cargo/bin
    • Add the directory ~/.cargo/bin to PATH (with the directions in this post)
I don’t think there’s anything wrong with doing what the tool suggests (it might be the “best way”!), but personally I usually use the second approach because I prefer knowing exactly what configuration I’m changing.

a note on fish_add_path

fish has a handy function called fish_add_path that you can run to add a directory to your PATH like this:
fish_add_path /some/directory
This will add the directory to your PATH, and automatically update all running fish shells with the new PATH. You don’t have to update your config at all! This is EXTREMELY convenient, but one downside (and the reason I’ve personally stopped using it) is that if you ever need to remove the directory from your PATH a few weeks or months later because maybe you made a mistake, it’s kind of hard to do (there are instructions in this comments of this github issue though).

that’s all

Hopefully this will help some people. Let me know (on Mastodon or Bluesky) if you there are other major gotchas that have tripped you up when adding a directory to your PATH, or if you have questions about this post!
2025-02-14 3 months ago / 未收藏/ MongoDB | Blog发送到 kindle
WhyHow.AI has built and open-sourced a platform using MongoDB, enhancing how organizations leverage knowledge graphs for data management and insights. Integrated with MongoDB, this solution offers a scalable foundation with features like vector search and aggregation to support organizations in their AI journey.
Knowledge graphs address the limitations of traditional retrieval-augmented generation (RAG) systems, which can struggle to capture intricate relationships and contextual nuances in enterprise data. By embedding rules and relationships into a graph structure, knowledge graphs enable accurate and deterministic retrieval processes. This functionality extends beyond information retrieval: knowledge graphs also serve as foundational elements for enterprise memory, helping organizations maintain structured datasets that support future model training and insights.
WhyHow.AI enhances this process by offering tools designed to combine large language model (LLM) workflows with Python- and JSON-native graph management. Using MongoDB’s robust capabilities, these tools help combine structured and unstructured data and search capabilities, enabling efficient querying and insights across diverse datasets. MongoDB’s modular architecture seamlessly integrates vector retrieval, full-text search, and graph structures, making it an ideal platform for RAG and unlocking the full potential of contextual data.
Check out our AI Learning Hub to learn more about building AI-powered apps with MongoDB.

Creating and storing knowledge graphs with WhyHow.AI and MongoDB

Creating effective knowledge graphs for RAG requires a structured approach that combines workflows from LLMs, developers, and nontechnical domain experts. Simply capturing all entities and relationships from text and relying on an LLM to organize the data can lead to a messy retrieval process that lacks utility. Instead, WhyHow.AI advocates for a schema-constrained graph creation method, emphasizing the importance of developing a context-specific schema tailored to the user’s use case. This approach ensures that the knowledge graphs focus on the specific relationships that matter most to the user’s workflow.
Once the knowledge graphs are created, the flexibility of MongoDB’s schema design ensures that users are not confined to rigid structures. This adaptability enables seamless expansion and evolution of knowledge graphs as data and use cases develop. Organizations can rapidly iterate during early application development without being restricted by predefined schemas. In instances where additional structure is required, MongoDB supports schema enforcement, offering a balance between flexibility and data integrity.
For instance, aligning external research with patient records is crucial to delivering personalized healthcare. Knowledge graphs bridge the gap between clinical trials, best practices, and individual patient histories. New clinical guidelines can be integrated with patient records to identify which patients would benefit most from updated treatments, ensuring that the latest practices are applied to individual care plans.

Optimizing knowledge graph storage and retrieval with MongoDB

Harnessing the full potential of knowledge graphs requires both effective creation tools and robust systems for storage and retrieval. Here’s how WhyHow.AI and MongoDB work together to optimize the management of knowledge graphs.

Storing data in MongoDB

WhyHow.AI relies on MongoDB’s document-oriented structure to organize knowledge graph data into modular, purpose-specific collections, enabling efficient and flexible queries. This approach is crucial for managing complex entity relationships and ensuring accurate provenance tracking.
To support this functionality, the WhyHow.AI Knowledge Graph Studio comprises several key components:
  • Workspaces separate documents, schemas, graphs, and associated data by project or domain, maintaining clarity and focus.
  • Chunks are raw text segments with embeddings for similarity searches, linked to triples and documents to provide evidence and provenance.
  • Graph collection stores the knowledge graph along with metadata and schema associations, all organized by workspace for centralized data management.
  • Schemas define the entities, relationships, and patterns within graphs, adapting dynamically to reflect new data and keep the graph relevant.
  • Nodes represent entities like people, locations, or concepts, each with unique identifiers and properties, forming the graph’s foundation.
  • Triples define subject-predicate-object relationships and store embedded vectors for similarity searches, enabling reliable retrieval of relevant facts.
  • Queries log user queries, including triple results and metadata, providing an immutable history for analysis and optimization.
Screenshot of a WhyHow.AI platform and knowledge graph illustration
Figure 1. WhyHow.AI platform and knowledge graph illustration.
To enhance data interoperability, MongoDB’s aggregation framework enables efficient linking across collections. For instance, retrieving chunks associated with a specific triple can be seamlessly achieved through an aggregation pipeline, connecting workspaces, graphs, chunks, and document collections into a cohesive data flow.

Querying knowledge graphs

With the representation established, users can perform both structured and unstructured queries with the WhyHow.AI querying system. Structured queries enable the selection of specific entity types and relationships, while unstructured queries enable natural language questions to return related nodes, triples, and linked vector chunks. WhyHow.AI’s query engine embeds triples to enhance retrieval accuracy, bypassing traditional Text2Cypher methods. Through a retrieval engine that embeds triples and enables users to retrieve embedded triples with chunks tied to them, WhyHow.AI uses the best of both structured and unstructured data structures and retrieval patterns. And, with MongoDB’s built-in vector search, users can store and query vectorized text chunks alongside their graph and application data in a single, unified location.

Enabling scalability, portability, and aggregations

MongoDB’s horizontal scalability ensures that knowledge graphs can grow effortlessly alongside expanding datasets. Users can also easily utilize WhyHow.AI's platform to create modular multiagent and multigraph workflows. They can deploy MongoDB Atlas on their preferred cloud provider or maintain control by running it in their own environments, gaining flexibility and reliability. As graph complexity increases, MongoDB’s aggregation framework facilitates diverse queries, extracting meaningful insights from multiple datasets with ease.

Providing familiarity and ease of use

MongoDB’s familiarity enables developers to apply their existing expertise without the need to learn new technologies or workflows. With WhyHow.AI and MongoDB, developers can build graphs with JSON data and Python-native APIs, which are perfect for LLM-driven workflows. The same database trusted for years in application development can now manage knowledge graphs, streamlining onboarding and accelerating development timelines.

Taking the next steps

WhyHow.AI’s knowledge graphs overcome the limitations of traditional RAG systems by structuring data into meaningful entities, relationships, and contexts. This enhances retrieval accuracy and decision-making in complex fields. Integrated with MongoDB, these capabilities are amplified through a flexible, scalable foundation featuring modular architecture, vector search, and powerful aggregation. Together, WhyHow.AI and MongoDB help organizations unlock their data’s potential, driving insights and enabling innovative knowledge management solutions.
No matter where you are in your AI journey, MongoDB can help! You can get started with your AI-powered apps by registering for MongoDB Atlas and exploring the tutorials available in our AI Learning Hub. Otherwise, head over to our quick-start guide to get started with MongoDB Atlas Vector Search today.
Want to learn more about why MongoDB is the best choice for supporting modern AI applications? Check out our on-demand webinar, “Comparing PostgreSQL vs. MongoDB: Which is Better for AI Workloads?” presented by MongoDB Field CTO, Rick Houlihan.
If your company is interested in being featured in a story like this, we’d love to hear from you. Reach out to us at ai_adopters@mongodb.com.
"James Graham" / 2025-02-14 3 months ago / 未收藏/ Mozilla Hacks – the Web developer blog发送到 kindle
Interop 2025 continues the mission to make the web more consistent across browsers, building on 2024’s 95% interoperability score. This year, 19 focus areas target key developer needs and long-standing issues, including WebRTC improvements, Storage Access API, and CSS Zoom.
The post Launching Interop 2025 appeared first on Mozilla Hacks - the Web developer blog.
"James Graham" / 2025-02-14 3 months ago / 未收藏/ Mozilla Hacks – the Web developer blog发送到 kindle
Interop 2025 continues the mission to make the web more consistent across browsers, building on 2024’s 95% interoperability score. This year, 19 focus areas target key developer needs and long-standing issues, including WebRTC improvements, Storage Access API, and CSS Zoom.
The post Launching Interop 2025 appeared first on Mozilla Hacks - the Web developer blog.
"Nefe Emadamerho-Atori" / 2025-02-14 3 months ago / 未收藏/ LogRocket - Medium发送到 kindle
Discover how the MERN stack (MongoDB, Express.js, React, Node.js) enables developers to build dynamic, performant, modern websites and apps.
The post What is the MERN stack? Overview with examples appeared first on LogRocket Blog.
"Bartosz Jaworski" / 2025-02-13 3 months ago / 未收藏/ LogRocket - Medium发送到 kindle
By implementing an effective PDM system, you can foster collaboration, improve decision-making, and accelerate your time-to-market.
The post A guide to product data management (PDM) appeared first on LogRocket Blog.
"David Omotayo" / 2025-02-13 3 months ago / 未收藏/ LogRocket - Medium发送到 kindle
Use parallel computing in Node.js with worker threads to optimize performance, handle CPU-intensive tasks, and utilize multi-core processors.
The post Leveraging parallel computing in Node.js appeared first on LogRocket Blog.