草庐IT

CMeKG代码解读(以项目为导向从零开始学习知识图谱)(一)

chen_nnn 2023-07-30 原文

作者从零开始学习和知识图谱有关技术和内容,而本文的核心内容是对CMeKG的python代码进行学习和解读,供大家讨论参考共同进步。

CMeKG(Chinese Medical Knowledge Graph)是利用自然语言处理与文本挖掘技术,基于大规模医学文本数据,以人机结合的方式研发的中文医学知识图谱。

目录

项目来源:

项目成果展示:

项目源代码获取:

medical_re.py

 config 类:

 IterableDataset 类:

 search():

process_data():

 get_stream():

Model4s类

__init__():

forward():


项目来源:

中文医学知识图谱CMeKG2.0版发布-自然语言处理实验室北京大学计算语言学研究所、郑州大学自然语言处理实验室与鹏城实验室人工智能研究中心智慧医疗课题组联合发布中文医学知识图谱CMeKG2.0版http://cmekg.pcl.ac.cn/ 欢迎大家试用并提出宝贵意见!  CMeKG(Chinese Medical Knowledge Graph)是利用自然语言处理与文本挖掘技术,基于大规模医学文本数据,以人机结合的方式研发的中文医学知识图谱。CMeKG的构建参考了ICD、ATC、SNOMED、MeSH等权威的国际医学标准以及规模庞大、多源...http://www5.zzu.edu.cn/nlp/info/1018/1785.htm

项目成果展示:

CMeKG中文医学知识图谱http://cmekg.pcl.ac.cn/

项目源代码获取:

https://github.com/king-yyf/CMeKG_toolshttps://github.com/king-yyf/CMeKG_tools

medical_re.py

 config 类:

class config:
    batch_size = 32
    max_seq_len = 256
    num_p = 23
    learning_rate = 1e-5
    EPOCH = 2

    PATH_SCHEMA = "/Users/yangyf/workplace/model/medical_re/predicate.json"
    PATH_TRAIN = '/Users/yangyf/workplace/model/medical_re/train_data.json'
    PATH_BERT = "/Users/yangyf/workplace/model/medical_re/"
    PATH_MODEL = "/Users/yangyf/workplace/model/medical_re/model_re.pkl"
    PATH_SAVE = '/content/model_re.pkl'
    tokenizer = BertTokenizer.from_pretrained("/Users/yangyf/workplace/model/medical_re/" + 'vocab.txt')

    id2predicate = {}
    predicate2id = {}

 此类中定义了部分基础变量,例如一个批的数据大小是32,最大字长是256等等。同时还定义了部分文件路径,在使用是需要根据自己电脑中文件的位置进行修改。

tokenizer = BerTokenizer.from_pretrained 是 transformers 当中的一个方法,该方法的工作流程是它会先判断 from_pretrained 函数的参数,如果是 PRETRAINED_MODEL_ARCHIVE_MAP 已有的,就会去cache里找;如果不是,就会判断它是不是一个路径,会在这个路径下找需要的文件,一个config文件和一个bin文件。

 PRETRAINED_MODEL_ARCHIVE_MAP = {
        'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased.tar.gz",
        'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased.tar.gz",
        'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased.tar.gz",
        'bert-base-multilingual': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual.tar.gz",
        'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese.tar.gz",
    }
"""
    Params:
    pretrained_model_name: either:
    - a str with the name of a pre-trained model to load selected in the list of:
    . `bert-base-uncased`
    . `bert-large-uncased`
    . `bert-base-cased`
    . `bert-base-multilingual`
    . `bert-base-chinese`
    - a path or url to a pretrained model archive containing:
    . `bert_config.json` a configuration file for the model
    . `pytorch_model.bin` a PyTorch dump of a BertForPreTraining instance
    *inputs, **kwargs: additional input for the specific Bert class
    (ex: num_labels for BertForSequenceClassification)
"""

 IterableDataset 类:

class IterableDataset(torch.utils.data.IterableDataset):
    def __init__(self, data, random):
        super(IterableDataset).__init__()
        self.data = data
        self.random = random
        self.tokenizer = config.tokenizer

    def __len__(self):
        return len(self.data)

    def search(self, sequence, pattern):
        n = len(pattern)
        for i in range(len(sequence)):
            if sequence[i:i + n] == pattern:
                return i
        return -1

    def process_data(self):
        idxs = list(range(len(self.data)))
        if self.random:
            np.random.shuffle(idxs)
        batch_size = config.batch_size
        max_seq_len = config.max_seq_len
        num_p = config.num_p
        batch_token_ids = np.zeros((batch_size, max_seq_len), dtype=np.int)         
        batch_mask_ids = np.zeros((batch_size, max_seq_len), dtype=np.int)          
        batch_segment_ids = np.zeros((batch_size, max_seq_len), dtype=np.int)
        batch_subject_ids = np.zeros((batch_size, 2), dtype=np.int)
        batch_subject_labels = np.zeros((batch_size, max_seq_len, 2), dtype=np.int)
        batch_object_labels = np.zeros((batch_size, max_seq_len, num_p, 2), dtype=np.int)
        batch_i = 0
        for i in idxs:
            text = self.data[i]['text']
            batch_token_ids[batch_i, :] = self.tokenizer.encode(text, max_length=max_seq_len, pad_to_max_length=True,add_special_tokens=True)
            batch_mask_ids[batch_i, :len(text) + 2] = 1
            spo_list = self.data[i]['spo_list']
            idx = np.random.randint(0, len(spo_list), size=1)[0]
            s_rand = self.tokenizer.encode(spo_list[idx][0])[1:-1]
            s_rand_idx = self.search(list(batch_token_ids[batch_i, :]), s_rand)
            batch_subject_ids[batch_i, :] = [s_rand_idx, s_rand_idx + len(s_rand) - 1]
            for i in range(len(spo_list)):
                spo = spo_list[i]
                s = self.tokenizer.encode(spo[0])[1:-1]
                p = config.prediction2id[spo[1]]
                o = self.tokenizer.encode(spo[2])[1:-1]
                s_idx = self.search(list(batch_token_ids[batch_i]), s)
                o_idx = self.search(list(batch_token_ids[batch_i]), o)
                if s_idx != -1 and o_idx != -1:#如果主体和客体都存在
                    batch_subject_labels[batch_i, s_idx, 0] = 1
                    batch_subject_labels[batch_i, s_idx + len(s) - 1, 1] = 1
                    if s_idx == s_rand_idx:
                        batch_object_labels[batch_i, o_idx, p, 0] = 1
                        batch_object_labels[batch_i, o_idx + len(o) - 1, p, 1] = 1
            batch_i += 1
            if batch_i == batch_size or i == idxs[-1]:
                yield batch_token_ids, batch_mask_ids, batch_segment_ids, batch_subject_labels, batch_subject_ids, batch_object_labels
                batch_token_ids[:, :] = 0
                batch_mask_ids[:, :] = 0
                batch_subject_ids[:, :] = 0
                batch_subject_labels[:, :, :] = 0
                batch_object_labels[:, :, :, :] = 0
                batch_i = 0



 search():

用来进行模式匹配,该函数中有两个输入参数,一个返回参数,在一给定的 sequence 序列中找到所希望的 pattern ,如果能找到则返回 pattern 开始的位置,找不到则返回-1。

process_data():

对数据进行处理和分类,便于以后使用。如果类中 random 参数为真,则会使用 shuffle 方法将idxs中的数据进行打乱打散。之后创建所需的标志和掩码数组。以前两条创建语句为例:生成一个batch_size,max_seq_len大小的零数组,数据的存储方式是numpy的整型形式,数组名称是token;生成和token同样大小的零数组,用于掩码,mask。

在 for 循环中首先从字典中取出所需处理的文本,然后使用 tokenizer.encode() 中进行解析,返回的是切分之后的编码情况。此处对 tokenizer.encode() 进行详细阐述。

一个简单的例子来展示 tokenizer.encode() 的作用,此部分来自天才小呵呵的博文:

Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode1 引言Hugging Face公司出的transformer包,能够超级方便的引入预训练模型,BERT、ALBERT、GPT2… tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') model = BertForTokenClassification.from_pretrained('bert-bas...https://blog.csdn.net/qq_33293040/article/details/105439750

sentence = "Hello, my sun is cuting."
input_ids_method1 = torch.tensor(
    tokenizer.encode(sentence, add_special_tokens=True))  # Batch size 1
    # tensor([ 101, 7592, 1010, 2026, 2365, 2003, 3013, 2075, 1012,  102])

代码运行后的输出包括101和102表示开头和结尾的输出[cls]、[sep]。附上 tokenizer.encode() 的源码解析各参数的作用。

    def encode(
        self,
        text: str,  # 需要转化的句子
        text_pair: Optional[str] = None,   
        add_special_tokens: bool = True, 
        max_length: Optional[int] = None,  
        stride: int = 0,
        truncation_strategy: str = "longest_first",
        pad_to_max_length: bool = False,
        return_tensors: Optional[str] = None,
        **kwargs
    ):
        """
        Converts a string in a sequence of ids (integer), using the tokenizer and vocabulary.

        Same as doing ``self.convert_tokens_to_ids(self.tokenize(text))``.

        Args:
            text (:obj:`str` or :obj:`List[str]`):
                The first sequence to be encoded. This can be a string, a list of strings (tokenized string using
                the `tokenize` method) or a list of integers (tokenized string ids using the `convert_tokens_to_ids`
                method)
            text_pair (:obj:`str` or :obj:`List[str]`, `optional`, defaults to :obj:`None`):
                Optional second sequence to be encoded. This can be a string, a list of strings (tokenized
                string using the `tokenize` method) or a list of integers (tokenized string ids using the
                `convert_tokens_to_ids` method)
            add_special_tokens (:obj:`bool`, `optional`, defaults to :obj:`True`):
                If set to ``True``, the sequences will be encoded with the special tokens relative
                to their model.
            max_length (:obj:`int`, `optional`, defaults to :obj:`None`):
                If set to a number, will limit the total sequence returned so that it has a maximum length.
                If there are overflowing tokens, those will be added to the returned dictionary
            stride (:obj:`int`, `optional`, defaults to ``0``):
                If set to a number along with max_length, the overflowing tokens returned will contain some tokens
                from the main sequence returned. The value of this argument defines the number of additional tokens.
            truncation_strategy (:obj:`str`, `optional`, defaults to `longest_first`):
                String selected in the following options:

                - 'longest_first' (default) Iteratively reduce the inputs sequence until the input is under max_length
                  starting from the longest one at each token (when there is a pair of input sequences)
                - 'only_first': Only truncate the first sequence
                - 'only_second': Only truncate the second sequence
                - 'do_not_truncate': Does not truncate (raise an error if the input sequence is longer than max_length)
            pad_to_max_length (:obj:`bool`, `optional`, defaults to :obj:`False`):
                If set to True, the returned sequences will be padded according to the model's padding side and
                padding index, up to their max length. If no max length is specified, the padding is done up to the
                model's max length. The tokenizer padding sides are handled by the class attribute `padding_side`
                which can be set to the following strings:

                - 'left': pads on the left of the sequences
                - 'right': pads on the right of the sequences
                Defaults to False: no padding.
            return_tensors (:obj:`str`, `optional`, defaults to :obj:`None`):
                Can be set to 'tf' or 'pt' to return respectively TensorFlow :obj:`tf.constant`
                or PyTorch :obj:`torch.Tensor` instead of a list of python integers.
            **kwargs: passed to the `self.tokenize()` method
        """
        encoded_inputs = self.encode_plus(
            text,
            text_pair=text_pair,
            max_length=max_length,
            add_special_tokens=add_special_tokens,
            stride=stride,
            truncation_strategy=truncation_strategy,
            pad_to_max_length=pad_to_max_length,
            return_tensors=return_tensors,
            **kwargs,
        )

        return encoded_inputs["input_ids"]

  • add_special_tokens: bool = True 将句子转化成对应模型的输入形式,默认开启

  •  max_length 设置最大长度,如果不设置的话原模型设置的最大长度是512,此时,如果句子长度超过512会报错:

  •  pad_to_max_length: bool = False 是否按照最长长度补齐,默认关闭,此处可以通过tokenizer.padding_side = 'left' 设置补齐的位置在左边插入。

  • truncation_strategy: str = "longest_first" 截断机制,有四种方式来读取句子内容:

    • ‘longest_first’(默认):一直迭代,读到不能再读,读满为止
    • ‘only_first’: 只读入第一个序列
    • ‘only_second’: 只读入第二个序列
    • ‘do_not_truncate’: 不做截取,长了就报错 
  • return_tensors: Optional[str] = None 返回的数据类型,默认是None,可以选择tensorflow版本 ('tf') 和pytorch版本('pt')。

 然后源码在mask列表里面选择第 batch_i 行,将里面文本长度加2的值置1表明文本长度,预留出开头和结尾的输出。spo是关系抽取中的三元组,s是主体,o是客体,p是关系。找到一个随机主体,然后在文本中进行匹配,找到该主体位置并记录。然后对spo_list进行遍历,如果主体、客体都在文本中存在,则记录下主体位置和关系,同时如果该主体与随机主体相同,则记录下客体的位置。

小循环的次数为spo_list的长度,大循环的结束条件有两个一是批数达到上限二是数据达到上限。

这里附上对于 yield 方法的解析:

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

 get_stream():

def get_stream(self):
    return cycle(self.process_data())

在此类中接下来两个函数,用来对上文中的process_data()函数进行循环无尽的生成,对于cycle函数的解释如下。

itertools. cycle ( iterable )
Make an iterator returning elements from the iterable and saving a copy of each. When the iterable is exhausted, return elements from the saved copy. Repeats indefinitely. Roughly equivalent to:
def cycle(iterable):
    # cycle('ABCD') --> A B C D A B C D A B C D ...
    saved = []
    for element in iterable:
        yield element
        saved.append(element)
    while saved:
        for element in saved:
              yield element 

Model4s类

class Model4s(nn.Module):
    def __init__(self, hidden_size=768):
        super(Model4s, self).__init__()
        self.bert = BertModel.from_pretrained(config.PATH_BERT)
        for param in self.bert.parameters():
            param.requires_grad = True
        self.dropout = nn.Dropout(p=0.2)
        self.linear = nn.Linear(in_features=hidden_size, out_features=2, bias=True)
        self.sigmoid = nn.Sigmoid()

    def forward(self, input_ids, input_mask, segment_ids, hidden_size=768):
        hidden_states = self.bert(input_ids,
                                  attention_mask=input_mask,
                                  token_type_ids=segment_ids)[0]  # (batch_size, sequence_length, hidden_size)
        output = self.sigmoid(self.linear(self.dropout(hidden_states))).pow(2)

        return output, hidden_states

 先从名字理解该类,此类用于主体数据模型的训练。

__init__():

在这里定义并初始化了一些以后会使用的函数。

super().__init__(),就是继承父类的init方法,关于super().__init__()的详细解释可以查看此篇博文,个人认为介绍的十分详细。python中super().__init__()_java/python知识分享-CSDN博客_super().__init__()super().__init__() 1、子类构造函数调用super().__init__()1.1、第一个直接继承父类,可以调用name1.2、第二个继承自父类,覆盖初始化化def init,增加属性age,不能调用name属性1.3、第三个继承自父类,覆盖初始化化def init,并继承初始化属性name,可以调用2、继承顺序3、python2和3的区别1、子类构造函数调用super().i...https://blog.csdn.net/a__int__/article/details/104600972

然后加载一个初始化的训练模型,路径在前面已经定义过了。 之后由于是最终的训练模型所以将require_grad设置为真。

根据PyTorch的自动求导机制,如果一个tensor设置require_grad为True的情况下,才会对这个tensor以及由这个tensor计算出来的其他tensor求导,并将导数值存在tensor的grad属性中,便于优化器来更新参数。

所以一般情况下,只有对训练过程的tensor设置require_grad为True,便于求导和更新参数。而验证过程只是检查当前模型的泛化能力,只需要求出loss,所以不需要根据val set的loss对参数进行更新,因此require_grad为False。

作者:zhFang1999
链接:https://www.zhihu.com/question/436410778/answer/1645941506
来源:知乎

对于nn.Dropout的解释可以参见这篇博文:深度学习中Dropout原理解析_Microstrong-CSDN博客_dropout1. Dropout简介1.1 Dropout出现的原因在机器学习的模型中,如果模型的参数太多,而训练样本又太少,训练出来的模型很容易产生过拟合的现象。在训练神经网络的时候经常会遇到过拟合的问题,过拟合具体表现在:模型在训练数据上损失函数较小,预测准确率较高;但是在测试数据上损失函数比较大,预测准确率较低。过拟合是很多机器学习的通病。如果模型过拟合,那么得到的模型几乎不能用。为了解决过拟合问题,一...https://blog.csdn.net/program_developer/article/details/80737724

 对于nn.Linear的解释可以参照这两篇博文:其中第二篇博客最清晰的解释了in_features和out_features,第一篇则较为全面的介绍了整个nn.linear的使用方式并给出公式说明。

Pytorch.nn.Linear 解析(数学角度)_Medlen-CSDN博客_nn.linearpytorch.nn.Linear 是一个类,下面是它的一些初始化参数in_features : 输入样本的张量大小out_features : 输出样本的张量大小bias : 偏置它主要是对输入数据做一个线性变换。y=xAT+by=xA^T+by=xAT+b这里A是权重矩阵,b是偏置。他们都是根据 in_features 生成测试代码:m = torch.nn.Linea...https://blog.csdn.net/weixin_38481963/article/details/105258389PyTorch的nn.Linear()详解_风雪夜归人o的博客-CSDN博客_nn.linear  PyTorch的nn.Linear()是用于设置网络中的全连接层的,需要注意的是全连接层的输入与输出都是二维张量,一般形状为[batch_size, size],不同于卷积层要求输入输出是四维张量。其用法与形参说明如下:  in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size。  out_features指的是输出的二维张量的大小,即...https://blog.csdn.net/qq_42079689/article/details/102873766?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

该函数中的最后一句引用了nn中的Sigmoid函数,而该函数形式为:

forward():

这个函数说实话还并没有看明白,在这个python文件中并没有使用,应结合在实际py文件中的使用,在此推测一下应该是使用初始化好的模型对主体进行相关计算,计算出隐藏层数,然后使用sigmoid计算出一个数字(经过平方),以供日后使用

有关CMeKG代码解读(以项目为导向从零开始学习知识图谱)(一)的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  4. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  5. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  8. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  9. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  10. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

随机推荐